From 22248dfb93f40d3f65459226e02d8b79dc537e1a Mon Sep 17 00:00:00 2001 From: Brad Kramer Date: Sun, 9 Apr 2017 19:59:23 -0400 Subject: [PATCH] Add project files. --- CPHelper/.svn/all-wcprops | 17 + CPHelper/.svn/desktop.ini | 5 + CPHelper/.svn/dir-prop-base | 5 + CPHelper/.svn/entries | 99 + CPHelper/.svn/prop-base/desktop.ini | 5 + CPHelper/.svn/props/desktop.ini | 5 + CPHelper/.svn/text-base/CPHelper.cs.svn-base | 1342 ++++++++++++++ .../.svn/text-base/CPHelper.csproj.svn-base | 71 + CPHelper/.svn/text-base/desktop.ini | 5 + CPHelper/.svn/tmp/desktop.ini | 5 + CPHelper/.svn/tmp/prop-base/desktop.ini | 5 + CPHelper/.svn/tmp/props/desktop.ini | 5 + CPHelper/.svn/tmp/text-base/desktop.ini | 5 + CPHelper/CPHelper.cs | 1342 ++++++++++++++ CPHelper/CPHelper.csproj | 98 + CPHelper/Properties/.svn/all-wcprops | 11 + CPHelper/Properties/.svn/desktop.ini | 5 + CPHelper/Properties/.svn/dir-prop-base | 5 + CPHelper/Properties/.svn/entries | 62 + .../Properties/.svn/prop-base/desktop.ini | 5 + CPHelper/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + .../Properties/.svn/text-base/desktop.ini | 5 + CPHelper/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + .../Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + CPHelper/Properties/AssemblyInfo.cs | 36 + CPHelper/Properties/desktop.ini | 5 + CPHelper/desktop.ini | 5 + CommandEcho/.svn/all-wcprops | 17 + CommandEcho/.svn/desktop.ini | 5 + CommandEcho/.svn/dir-prop-base | 5 + CommandEcho/.svn/entries | 99 + CommandEcho/.svn/prop-base/desktop.ini | 5 + CommandEcho/.svn/props/desktop.ini | 5 + .../.svn/text-base/CommandEcho.cs.svn-base | 47 + .../text-base/CommandEcho.csproj.svn-base | 63 + CommandEcho/.svn/text-base/desktop.ini | 5 + CommandEcho/.svn/tmp/desktop.ini | 5 + CommandEcho/.svn/tmp/prop-base/desktop.ini | 5 + CommandEcho/.svn/tmp/props/desktop.ini | 5 + CommandEcho/.svn/tmp/text-base/desktop.ini | 5 + CommandEcho/CommandEcho.cs | 47 + CommandEcho/CommandEcho.csproj | 90 + CommandEcho/Properties/.svn/all-wcprops | 11 + CommandEcho/Properties/.svn/desktop.ini | 5 + CommandEcho/Properties/.svn/dir-prop-base | 5 + CommandEcho/Properties/.svn/entries | 62 + .../Properties/.svn/prop-base/desktop.ini | 5 + CommandEcho/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + .../Properties/.svn/text-base/desktop.ini | 5 + CommandEcho/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + .../Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + CommandEcho/Properties/AssemblyInfo.cs | 36 + CommandEcho/Properties/desktop.ini | 5 + CommandEcho/desktop.ini | 5 + GMCPEcho/.svn/all-wcprops | 17 + GMCPEcho/.svn/desktop.ini | 5 + GMCPEcho/.svn/dir-prop-base | 5 + GMCPEcho/.svn/entries | 99 + GMCPEcho/.svn/prop-base/desktop.ini | 5 + GMCPEcho/.svn/props/desktop.ini | 5 + GMCPEcho/.svn/text-base/GMCPEcho.cs.svn-base | 71 + .../.svn/text-base/GMCPEcho.csproj.svn-base | 63 + GMCPEcho/.svn/text-base/desktop.ini | 5 + GMCPEcho/.svn/tmp/desktop.ini | 5 + GMCPEcho/.svn/tmp/prop-base/desktop.ini | 5 + GMCPEcho/.svn/tmp/props/desktop.ini | 5 + GMCPEcho/.svn/tmp/text-base/desktop.ini | 5 + GMCPEcho/GMCPEcho.cs | 71 + GMCPEcho/GMCPEcho.csproj | 90 + GMCPEcho/Properties/.svn/all-wcprops | 11 + GMCPEcho/Properties/.svn/desktop.ini | 5 + GMCPEcho/Properties/.svn/dir-prop-base | 5 + GMCPEcho/Properties/.svn/entries | 62 + .../Properties/.svn/prop-base/desktop.ini | 5 + GMCPEcho/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + .../Properties/.svn/text-base/desktop.ini | 5 + GMCPEcho/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + .../Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + GMCPEcho/Properties/AssemblyInfo.cs | 36 + GMCPEcho/Properties/desktop.ini | 5 + GMCPEcho/desktop.ini | 5 + GQPredict/.svn/all-wcprops | 17 + GQPredict/.svn/desktop.ini | 5 + GQPredict/.svn/dir-prop-base | 5 + GQPredict/.svn/entries | 99 + GQPredict/.svn/prop-base/desktop.ini | 5 + GQPredict/.svn/props/desktop.ini | 5 + .../.svn/text-base/GQPredict.cs.svn-base | 151 ++ .../.svn/text-base/GQPredict.csproj.svn-base | 63 + GQPredict/.svn/text-base/desktop.ini | 5 + GQPredict/.svn/tmp/desktop.ini | 5 + GQPredict/.svn/tmp/prop-base/desktop.ini | 5 + GQPredict/.svn/tmp/props/desktop.ini | 5 + GQPredict/.svn/tmp/text-base/desktop.ini | 5 + GQPredict/GQPredict.cs | 151 ++ GQPredict/GQPredict.csproj | 90 + GQPredict/Properties/.svn/all-wcprops | 11 + GQPredict/Properties/.svn/desktop.ini | 5 + GQPredict/Properties/.svn/dir-prop-base | 5 + GQPredict/Properties/.svn/entries | 62 + .../Properties/.svn/prop-base/desktop.ini | 5 + GQPredict/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + .../Properties/.svn/text-base/desktop.ini | 5 + GQPredict/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + .../Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + GQPredict/Properties/AssemblyInfo.cs | 36 + GQPredict/Properties/desktop.ini | 5 + GQPredict/desktop.ini | 5 + Jayrock.Json.dll | Bin 0 -> 86016 bytes Mapper/.svn/all-wcprops | 47 + Mapper/.svn/desktop.ini | 5 + Mapper/.svn/dir-prop-base | 5 + Mapper/.svn/entries | 275 +++ Mapper/.svn/prop-base/desktop.ini | 5 + Mapper/.svn/props/desktop.ini | 5 + Mapper/.svn/text-base/Area.cs.svn-base | 147 ++ Mapper/.svn/text-base/Edit.cs.svn-base | 497 +++++ Mapper/.svn/text-base/Exit.cs.svn-base | 136 ++ Mapper/.svn/text-base/Mapper.cs.svn-base | 1607 +++++++++++++++++ Mapper/.svn/text-base/Mapper.csproj.svn-base | 77 + Mapper/.svn/text-base/Pathfind.cs.svn-base | 451 +++++ Mapper/.svn/text-base/Room.cs.svn-base | 256 +++ Mapper/.svn/text-base/desktop.ini | 5 + Mapper/.svn/tmp/desktop.ini | 5 + Mapper/.svn/tmp/prop-base/desktop.ini | 5 + Mapper/.svn/tmp/props/desktop.ini | 5 + Mapper/.svn/tmp/text-base/desktop.ini | 5 + Mapper/Area.cs | 147 ++ Mapper/Edit.cs | 497 +++++ Mapper/Exit.cs | 136 ++ Mapper/Finders/.svn/all-wcprops | 35 + Mapper/Finders/.svn/desktop.ini | 5 + Mapper/Finders/.svn/dir-prop-base | 5 + Mapper/Finders/.svn/entries | 198 ++ Mapper/Finders/.svn/prop-base/desktop.ini | 5 + Mapper/Finders/.svn/props/desktop.ini | 5 + .../Finders/.svn/text-base/Area.cs.svn-base | 23 + .../Finders/.svn/text-base/Entry.cs.svn-base | 23 + .../Finders/.svn/text-base/Name.cs.svn-base | 105 ++ .../.svn/text-base/Unmapped.cs.svn-base | 55 + .../.svn/text-base/VisitAll.cs.svn-base | 189 ++ Mapper/Finders/.svn/text-base/desktop.ini | 5 + Mapper/Finders/.svn/tmp/desktop.ini | 5 + Mapper/Finders/.svn/tmp/prop-base/desktop.ini | 5 + Mapper/Finders/.svn/tmp/props/desktop.ini | 5 + Mapper/Finders/.svn/tmp/text-base/desktop.ini | 5 + Mapper/Finders/Area.cs | 23 + Mapper/Finders/Entry.cs | 23 + Mapper/Finders/Name.cs | 105 ++ Mapper/Finders/Unmapped.cs | 55 + Mapper/Finders/VisitAll.cs | 189 ++ Mapper/Finders/desktop.ini | 5 + Mapper/Mapper.cs | 1607 +++++++++++++++++ Mapper/Mapper.csproj | 104 ++ Mapper/Pathfind.cs | 451 +++++ Mapper/Properties/.svn/all-wcprops | 11 + Mapper/Properties/.svn/desktop.ini | 5 + Mapper/Properties/.svn/dir-prop-base | 5 + Mapper/Properties/.svn/entries | 62 + Mapper/Properties/.svn/prop-base/desktop.ini | 5 + Mapper/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + Mapper/Properties/.svn/text-base/desktop.ini | 5 + Mapper/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + Mapper/Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + Mapper/Properties/AssemblyInfo.cs | 36 + Mapper/Properties/desktop.ini | 5 + Mapper/Room.cs | 256 +++ Mapper/Scripting/.svn/all-wcprops | 11 + Mapper/Scripting/.svn/desktop.ini | 5 + Mapper/Scripting/.svn/dir-prop-base | 5 + Mapper/Scripting/.svn/entries | 62 + Mapper/Scripting/.svn/prop-base/desktop.ini | 5 + Mapper/Scripting/.svn/props/desktop.ini | 5 + .../.svn/text-base/AreaScript.cs.svn-base | 103 ++ Mapper/Scripting/.svn/text-base/desktop.ini | 5 + Mapper/Scripting/.svn/tmp/desktop.ini | 5 + .../Scripting/.svn/tmp/prop-base/desktop.ini | 5 + Mapper/Scripting/.svn/tmp/props/desktop.ini | 5 + .../Scripting/.svn/tmp/text-base/desktop.ini | 5 + Mapper/Scripting/AreaScript.cs | 103 ++ Mapper/Scripting/desktop.ini | 5 + Mapper/desktop.ini | 5 + MobDB/.svn/all-wcprops | 23 + MobDB/.svn/desktop.ini | 5 + MobDB/.svn/dir-prop-base | 5 + MobDB/.svn/entries | 133 ++ MobDB/.svn/prop-base/desktop.ini | 5 + MobDB/.svn/props/desktop.ini | 5 + MobDB/.svn/text-base/Mob.cs.svn-base | 207 +++ MobDB/.svn/text-base/MobDB.cs.svn-base | 1210 +++++++++++++ MobDB/.svn/text-base/MobDB.csproj.svn-base | 67 + MobDB/.svn/text-base/desktop.ini | 5 + MobDB/.svn/tmp/desktop.ini | 5 + MobDB/.svn/tmp/prop-base/desktop.ini | 5 + MobDB/.svn/tmp/props/desktop.ini | 5 + MobDB/.svn/tmp/text-base/desktop.ini | 5 + MobDB/Mob.cs | 207 +++ MobDB/MobDB.cs | 1210 +++++++++++++ MobDB/MobDB.csproj | 94 + MobDB/Properties/.svn/all-wcprops | 11 + MobDB/Properties/.svn/desktop.ini | 5 + MobDB/Properties/.svn/dir-prop-base | 5 + MobDB/Properties/.svn/entries | 62 + MobDB/Properties/.svn/prop-base/desktop.ini | 5 + MobDB/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + MobDB/Properties/.svn/text-base/desktop.ini | 5 + MobDB/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + MobDB/Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + MobDB/Properties/AssemblyInfo.cs | 36 + MobDB/Properties/desktop.ini | 5 + MobDB/desktop.ini | 5 + MoonScript/.svn/all-wcprops | 17 + MoonScript/.svn/desktop.ini | 5 + MoonScript/.svn/dir-prop-base | 5 + MoonScript/.svn/entries | 99 + MoonScript/.svn/prop-base/desktop.ini | 5 + MoonScript/.svn/props/desktop.ini | 5 + .../.svn/text-base/MoonScript.cs.svn-base | 329 ++++ .../.svn/text-base/MoonScript.csproj.svn-base | 63 + MoonScript/.svn/text-base/desktop.ini | 5 + MoonScript/.svn/tmp/desktop.ini | 5 + MoonScript/.svn/tmp/prop-base/desktop.ini | 5 + MoonScript/.svn/tmp/props/desktop.ini | 5 + MoonScript/.svn/tmp/text-base/desktop.ini | 5 + MoonScript/MoonScript.cs | 329 ++++ MoonScript/MoonScript.csproj | 90 + MoonScript/Properties/.svn/all-wcprops | 11 + MoonScript/Properties/.svn/desktop.ini | 5 + MoonScript/Properties/.svn/dir-prop-base | 5 + MoonScript/Properties/.svn/entries | 62 + .../Properties/.svn/prop-base/desktop.ini | 5 + MoonScript/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + .../Properties/.svn/text-base/desktop.ini | 5 + MoonScript/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + .../Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + MoonScript/Properties/AssemblyInfo.cs | 36 + MoonScript/Properties/desktop.ini | 5 + MoonScript/desktop.ini | 5 + MudLog/.svn/all-wcprops | 17 + MudLog/.svn/desktop.ini | 5 + MudLog/.svn/dir-prop-base | 5 + MudLog/.svn/entries | 99 + MudLog/.svn/prop-base/desktop.ini | 5 + MudLog/.svn/props/desktop.ini | 5 + MudLog/.svn/text-base/MudLog.cs.svn-base | 109 ++ MudLog/.svn/text-base/MudLog.csproj.svn-base | 63 + MudLog/.svn/text-base/desktop.ini | 5 + MudLog/.svn/tmp/desktop.ini | 5 + MudLog/.svn/tmp/prop-base/desktop.ini | 5 + MudLog/.svn/tmp/props/desktop.ini | 5 + MudLog/.svn/tmp/text-base/desktop.ini | 5 + MudLog/MudLog.cs | 109 ++ MudLog/MudLog.csproj | 90 + MudLog/Properties/.svn/all-wcprops | 11 + MudLog/Properties/.svn/desktop.ini | 5 + MudLog/Properties/.svn/dir-prop-base | 5 + MudLog/Properties/.svn/entries | 62 + MudLog/Properties/.svn/prop-base/desktop.ini | 5 + MudLog/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + MudLog/Properties/.svn/text-base/desktop.ini | 5 + MudLog/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + MudLog/Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + MudLog/Properties/AssemblyInfo.cs | 36 + MudLog/Properties/desktop.ini | 5 + MudLog/desktop.ini | 5 + MySQL/.svn/all-wcprops | 29 + MySQL/.svn/desktop.ini | 5 + MySQL/.svn/dir-prop-base | 5 + MySQL/.svn/entries | 167 ++ .../prop-base/MySql.Data.Entity.dll.svn-base | 5 + MySQL/.svn/prop-base/MySql.Data.dll.svn-base | 5 + MySQL/.svn/prop-base/desktop.ini | 5 + MySQL/.svn/props/desktop.ini | 5 + MySQL/.svn/text-base/MySQL.cs.svn-base | 450 +++++ MySQL/.svn/text-base/MySQL.csproj.svn-base | 67 + .../text-base/MySql.Data.Entity.dll.svn-base | Bin 0 -> 229888 bytes MySQL/.svn/text-base/MySql.Data.dll.svn-base | Bin 0 -> 369152 bytes MySQL/.svn/text-base/desktop.ini | 5 + MySQL/.svn/tmp/desktop.ini | 5 + MySQL/.svn/tmp/prop-base/desktop.ini | 5 + MySQL/.svn/tmp/props/desktop.ini | 5 + MySQL/.svn/tmp/text-base/desktop.ini | 5 + MySQL/MySQL.cs | 450 +++++ MySQL/MySQL.csproj | 94 + MySQL/MySql.Data.Entity.dll | Bin 0 -> 229888 bytes MySQL/MySql.Data.dll | Bin 0 -> 369152 bytes MySQL/Properties/.svn/all-wcprops | 11 + MySQL/Properties/.svn/desktop.ini | 5 + MySQL/Properties/.svn/dir-prop-base | 5 + MySQL/Properties/.svn/entries | 62 + MySQL/Properties/.svn/prop-base/desktop.ini | 5 + MySQL/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + MySQL/Properties/.svn/text-base/desktop.ini | 5 + MySQL/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + MySQL/Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + MySQL/Properties/AssemblyInfo.cs | 36 + MySQL/Properties/desktop.ini | 5 + MySQL/desktop.ini | 5 + ProxyCore.dll | Bin 0 -> 48640 bytes ProxyCore.xml | 915 ++++++++++ ProxyCore/.svn/all-wcprops | 23 + ProxyCore/.svn/desktop.ini | 5 + ProxyCore/.svn/dir-prop-base | 5 + ProxyCore/.svn/entries | 148 ++ ProxyCore/.svn/prop-base/desktop.ini | 5 + ProxyCore/.svn/props/desktop.ini | 5 + ProxyCore/.svn/text-base/Log.cs.svn-base | 52 + .../.svn/text-base/ProxyCore.csproj.svn-base | 84 + ProxyCore/.svn/text-base/World.cs.svn-base | 647 +++++++ ProxyCore/.svn/text-base/desktop.ini | 5 + ProxyCore/.svn/tmp/desktop.ini | 5 + ProxyCore/.svn/tmp/prop-base/desktop.ini | 5 + ProxyCore/.svn/tmp/props/desktop.ini | 5 + ProxyCore/.svn/tmp/text-base/desktop.ini | 5 + ProxyCore/Input/.svn/all-wcprops | 23 + ProxyCore/Input/.svn/desktop.ini | 5 + ProxyCore/Input/.svn/dir-prop-base | 5 + ProxyCore/Input/.svn/entries | 130 ++ ProxyCore/Input/.svn/prop-base/desktop.ini | 5 + ProxyCore/Input/.svn/props/desktop.ini | 5 + .../.svn/text-base/InputData.cs.svn-base | 66 + .../.svn/text-base/InputEntry.cs.svn-base | 56 + .../.svn/text-base/InputHandler.cs.svn-base | 285 +++ ProxyCore/Input/.svn/text-base/desktop.ini | 5 + ProxyCore/Input/.svn/tmp/desktop.ini | 5 + .../Input/.svn/tmp/prop-base/desktop.ini | 5 + ProxyCore/Input/.svn/tmp/props/desktop.ini | 5 + .../Input/.svn/tmp/text-base/desktop.ini | 5 + ProxyCore/Input/InputData.cs | 66 + ProxyCore/Input/InputEntry.cs | 56 + ProxyCore/Input/InputHandler.cs | 285 +++ ProxyCore/Input/desktop.ini | 5 + ProxyCore/Log.cs | 52 + ProxyCore/Messages/.svn/all-wcprops | 11 + ProxyCore/Messages/.svn/desktop.ini | 5 + ProxyCore/Messages/.svn/dir-prop-base | 5 + ProxyCore/Messages/.svn/entries | 62 + ProxyCore/Messages/.svn/prop-base/desktop.ini | 5 + ProxyCore/Messages/.svn/props/desktop.ini | 5 + .../.svn/text-base/Message.cs.svn-base | 91 + ProxyCore/Messages/.svn/text-base/desktop.ini | 5 + ProxyCore/Messages/.svn/tmp/desktop.ini | 5 + .../Messages/.svn/tmp/prop-base/desktop.ini | 5 + ProxyCore/Messages/.svn/tmp/props/desktop.ini | 5 + .../Messages/.svn/tmp/text-base/desktop.ini | 5 + ProxyCore/Messages/Message.cs | 91 + ProxyCore/Messages/desktop.ini | 5 + ProxyCore/Output/.svn/all-wcprops | 23 + ProxyCore/Output/.svn/desktop.ini | 5 + ProxyCore/Output/.svn/dir-prop-base | 5 + ProxyCore/Output/.svn/entries | 130 ++ ProxyCore/Output/.svn/prop-base/desktop.ini | 5 + ProxyCore/Output/.svn/props/desktop.ini | 5 + .../.svn/text-base/TriggerData.cs.svn-base | 85 + .../.svn/text-base/TriggerEntry.cs.svn-base | 66 + .../.svn/text-base/TriggerHandler.cs.svn-base | 345 ++++ ProxyCore/Output/.svn/text-base/desktop.ini | 5 + ProxyCore/Output/.svn/tmp/desktop.ini | 5 + .../Output/.svn/tmp/prop-base/desktop.ini | 5 + ProxyCore/Output/.svn/tmp/props/desktop.ini | 5 + .../Output/.svn/tmp/text-base/desktop.ini | 5 + ProxyCore/Output/TriggerData.cs | 85 + ProxyCore/Output/TriggerEntry.cs | 66 + ProxyCore/Output/TriggerHandler.cs | 345 ++++ ProxyCore/Output/desktop.ini | 5 + ProxyCore/Properties/.svn/all-wcprops | 11 + ProxyCore/Properties/.svn/desktop.ini | 5 + ProxyCore/Properties/.svn/dir-prop-base | 5 + ProxyCore/Properties/.svn/entries | 62 + .../Properties/.svn/prop-base/desktop.ini | 5 + ProxyCore/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + .../Properties/.svn/text-base/desktop.ini | 5 + ProxyCore/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + .../Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + ProxyCore/Properties/AssemblyInfo.cs | 36 + ProxyCore/Properties/desktop.ini | 5 + ProxyCore/ProxyCore.csproj | 112 ++ ProxyCore/Scripting/.svn/all-wcprops | 17 + ProxyCore/Scripting/.svn/desktop.ini | 5 + ProxyCore/Scripting/.svn/dir-prop-base | 5 + ProxyCore/Scripting/.svn/entries | 96 + .../Scripting/.svn/prop-base/desktop.ini | 5 + ProxyCore/Scripting/.svn/props/desktop.ini | 5 + .../.svn/text-base/Plugin.cs.svn-base | 400 ++++ .../.svn/text-base/PluginMgr.cs.svn-base | 103 ++ .../Scripting/.svn/text-base/desktop.ini | 5 + ProxyCore/Scripting/.svn/tmp/desktop.ini | 5 + .../Scripting/.svn/tmp/prop-base/desktop.ini | 5 + .../Scripting/.svn/tmp/props/desktop.ini | 5 + .../Scripting/.svn/tmp/text-base/desktop.ini | 5 + ProxyCore/Scripting/Plugin.cs | 400 ++++ ProxyCore/Scripting/PluginMgr.cs | 103 ++ ProxyCore/Scripting/desktop.ini | 5 + ProxyCore/Utility/.svn/all-wcprops | 35 + ProxyCore/Utility/.svn/desktop.ini | 5 + ProxyCore/Utility/.svn/dir-prop-base | 5 + ProxyCore/Utility/.svn/entries | 198 ++ ProxyCore/Utility/.svn/prop-base/desktop.ini | 5 + ProxyCore/Utility/.svn/props/desktop.ini | 5 + .../Utility/.svn/text-base/Colors.cs.svn-base | 748 ++++++++ .../Utility/.svn/text-base/Config.cs.svn-base | 333 ++++ .../Utility/.svn/text-base/JSON.cs.svn-base | 54 + .../.svn/text-base/ServerConfig.cs.svn-base | 46 + .../Utility/.svn/text-base/Wrap.cs.svn-base | 141 ++ ProxyCore/Utility/.svn/text-base/desktop.ini | 5 + ProxyCore/Utility/.svn/tmp/desktop.ini | 5 + .../Utility/.svn/tmp/prop-base/desktop.ini | 5 + ProxyCore/Utility/.svn/tmp/props/desktop.ini | 5 + .../Utility/.svn/tmp/text-base/desktop.ini | 5 + ProxyCore/Utility/Colors.cs | 748 ++++++++ ProxyCore/Utility/Config.cs | 333 ++++ ProxyCore/Utility/JSON.cs | 54 + ProxyCore/Utility/ServerConfig.cs | 46 + ProxyCore/Utility/Wrap.cs | 141 ++ ProxyCore/Utility/desktop.ini | 5 + ProxyCore/World.cs | 647 +++++++ ProxyCore/desktop.ini | 5 + ProxyMud.exe | Bin 0 -> 22016 bytes ProxyMud.sln | 124 ++ ProxyMud/.svn/all-wcprops | 17 + ProxyMud/.svn/desktop.ini | 5 + ProxyMud/.svn/dir-prop-base | 5 + ProxyMud/.svn/entries | 102 ++ ProxyMud/.svn/prop-base/desktop.ini | 5 + ProxyMud/.svn/props/desktop.ini | 5 + ProxyMud/.svn/text-base/Program.cs.svn-base | 151 ++ .../.svn/text-base/ProxyMud.csproj.svn-base | 72 + ProxyMud/.svn/text-base/desktop.ini | 5 + ProxyMud/.svn/tmp/desktop.ini | 5 + ProxyMud/.svn/tmp/prop-base/desktop.ini | 5 + ProxyMud/.svn/tmp/props/desktop.ini | 5 + ProxyMud/.svn/tmp/text-base/desktop.ini | 5 + ProxyMud/Network/.svn/all-wcprops | 35 + ProxyMud/Network/.svn/desktop.ini | 5 + ProxyMud/Network/.svn/dir-prop-base | 5 + ProxyMud/Network/.svn/entries | 198 ++ ProxyMud/Network/.svn/prop-base/desktop.ini | 5 + ProxyMud/Network/.svn/props/desktop.ini | 5 + .../text-base/NetworkAardwolf.cs.svn-base | 191 ++ .../.svn/text-base/NetworkBase.cs.svn-base | 344 ++++ .../.svn/text-base/NetworkClient.cs.svn-base | 227 +++ .../.svn/text-base/NetworkServer.cs.svn-base | 257 +++ .../.svn/text-base/TelnetPacket.cs.svn-base | 61 + ProxyMud/Network/.svn/text-base/desktop.ini | 5 + ProxyMud/Network/.svn/tmp/desktop.ini | 5 + .../Network/.svn/tmp/prop-base/desktop.ini | 5 + ProxyMud/Network/.svn/tmp/props/desktop.ini | 5 + .../Network/.svn/tmp/text-base/desktop.ini | 5 + ProxyMud/Network/NetworkAardwolf.cs | 191 ++ ProxyMud/Network/NetworkBase.cs | 344 ++++ ProxyMud/Network/NetworkClient.cs | 227 +++ ProxyMud/Network/NetworkServer.cs | 259 +++ ProxyMud/Network/TelnetPacket.cs | 61 + ProxyMud/Network/desktop.ini | 5 + ProxyMud/Program.cs | 151 ++ ProxyMud/Properties/.svn/all-wcprops | 11 + ProxyMud/Properties/.svn/desktop.ini | 5 + ProxyMud/Properties/.svn/dir-prop-base | 5 + ProxyMud/Properties/.svn/entries | 62 + .../Properties/.svn/prop-base/desktop.ini | 5 + ProxyMud/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + .../Properties/.svn/text-base/desktop.ini | 5 + ProxyMud/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + .../Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + ProxyMud/Properties/AssemblyInfo.cs | 36 + ProxyMud/Properties/desktop.ini | 5 + ProxyMud/ProxyMud.csproj | 99 + ProxyMud/desktop.ini | 5 + Resources/.svn/all-wcprops | 23 + Resources/.svn/desktop.ini | 5 + Resources/.svn/dir-prop-base | 5 + Resources/.svn/entries | 130 ++ .../.svn/prop-base/Jayrock.Json.dll.svn-base | 5 + Resources/.svn/prop-base/Jayrock.dll.svn-base | 5 + Resources/.svn/prop-base/desktop.ini | 5 + Resources/.svn/prop-base/zlib.dll.svn-base | 5 + Resources/.svn/props/desktop.ini | 5 + .../.svn/text-base/Jayrock.Json.dll.svn-base | Bin 0 -> 86016 bytes Resources/.svn/text-base/Jayrock.dll.svn-base | Bin 0 -> 86016 bytes Resources/.svn/text-base/desktop.ini | 5 + Resources/.svn/text-base/zlib.dll.svn-base | Bin 0 -> 65536 bytes Resources/.svn/tmp/desktop.ini | 5 + Resources/.svn/tmp/prop-base/desktop.ini | 5 + Resources/.svn/tmp/props/desktop.ini | 5 + Resources/.svn/tmp/text-base/desktop.ini | 5 + Resources/Jayrock.Json.dll | Bin 0 -> 86016 bytes Resources/Jayrock.dll | Bin 0 -> 86016 bytes Resources/desktop.ini | 5 + Resources/zlib.dll | Bin 0 -> 65536 bytes StayAlive/.svn/all-wcprops | 17 + StayAlive/.svn/desktop.ini | 5 + StayAlive/.svn/dir-prop-base | 5 + StayAlive/.svn/entries | 99 + StayAlive/.svn/prop-base/desktop.ini | 5 + StayAlive/.svn/props/desktop.ini | 5 + .../.svn/text-base/StayAlive.cs.svn-base | 110 ++ .../.svn/text-base/StayAlive.csproj.svn-base | 63 + StayAlive/.svn/text-base/desktop.ini | 5 + StayAlive/.svn/tmp/desktop.ini | 5 + StayAlive/.svn/tmp/prop-base/desktop.ini | 5 + StayAlive/.svn/tmp/props/desktop.ini | 5 + StayAlive/.svn/tmp/text-base/desktop.ini | 5 + StayAlive/Properties/.svn/all-wcprops | 11 + StayAlive/Properties/.svn/desktop.ini | 5 + StayAlive/Properties/.svn/dir-prop-base | 5 + StayAlive/Properties/.svn/entries | 62 + .../Properties/.svn/prop-base/desktop.ini | 5 + StayAlive/Properties/.svn/props/desktop.ini | 5 + .../.svn/text-base/AssemblyInfo.cs.svn-base | 36 + .../Properties/.svn/text-base/desktop.ini | 5 + StayAlive/Properties/.svn/tmp/desktop.ini | 5 + .../Properties/.svn/tmp/prop-base/desktop.ini | 5 + .../Properties/.svn/tmp/props/desktop.ini | 5 + .../Properties/.svn/tmp/text-base/desktop.ini | 5 + StayAlive/Properties/AssemblyInfo.cs | 36 + StayAlive/Properties/desktop.ini | 5 + StayAlive/StayAlive.cs | 110 ++ StayAlive/StayAlive.csproj | 90 + StayAlive/desktop.ini | 5 + desktop.ini | 5 + zlib.dll | Bin 0 -> 65536 bytes 554 files changed, 34345 insertions(+) create mode 100644 CPHelper/.svn/all-wcprops create mode 100644 CPHelper/.svn/desktop.ini create mode 100644 CPHelper/.svn/dir-prop-base create mode 100644 CPHelper/.svn/entries create mode 100644 CPHelper/.svn/prop-base/desktop.ini create mode 100644 CPHelper/.svn/props/desktop.ini create mode 100644 CPHelper/.svn/text-base/CPHelper.cs.svn-base create mode 100644 CPHelper/.svn/text-base/CPHelper.csproj.svn-base create mode 100644 CPHelper/.svn/text-base/desktop.ini create mode 100644 CPHelper/.svn/tmp/desktop.ini create mode 100644 CPHelper/.svn/tmp/prop-base/desktop.ini create mode 100644 CPHelper/.svn/tmp/props/desktop.ini create mode 100644 CPHelper/.svn/tmp/text-base/desktop.ini create mode 100644 CPHelper/CPHelper.cs create mode 100644 CPHelper/CPHelper.csproj create mode 100644 CPHelper/Properties/.svn/all-wcprops create mode 100644 CPHelper/Properties/.svn/desktop.ini create mode 100644 CPHelper/Properties/.svn/dir-prop-base create mode 100644 CPHelper/Properties/.svn/entries create mode 100644 CPHelper/Properties/.svn/prop-base/desktop.ini create mode 100644 CPHelper/Properties/.svn/props/desktop.ini create mode 100644 CPHelper/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 CPHelper/Properties/.svn/text-base/desktop.ini create mode 100644 CPHelper/Properties/.svn/tmp/desktop.ini create mode 100644 CPHelper/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 CPHelper/Properties/.svn/tmp/props/desktop.ini create mode 100644 CPHelper/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 CPHelper/Properties/AssemblyInfo.cs create mode 100644 CPHelper/Properties/desktop.ini create mode 100644 CPHelper/desktop.ini create mode 100644 CommandEcho/.svn/all-wcprops create mode 100644 CommandEcho/.svn/desktop.ini create mode 100644 CommandEcho/.svn/dir-prop-base create mode 100644 CommandEcho/.svn/entries create mode 100644 CommandEcho/.svn/prop-base/desktop.ini create mode 100644 CommandEcho/.svn/props/desktop.ini create mode 100644 CommandEcho/.svn/text-base/CommandEcho.cs.svn-base create mode 100644 CommandEcho/.svn/text-base/CommandEcho.csproj.svn-base create mode 100644 CommandEcho/.svn/text-base/desktop.ini create mode 100644 CommandEcho/.svn/tmp/desktop.ini create mode 100644 CommandEcho/.svn/tmp/prop-base/desktop.ini create mode 100644 CommandEcho/.svn/tmp/props/desktop.ini create mode 100644 CommandEcho/.svn/tmp/text-base/desktop.ini create mode 100644 CommandEcho/CommandEcho.cs create mode 100644 CommandEcho/CommandEcho.csproj create mode 100644 CommandEcho/Properties/.svn/all-wcprops create mode 100644 CommandEcho/Properties/.svn/desktop.ini create mode 100644 CommandEcho/Properties/.svn/dir-prop-base create mode 100644 CommandEcho/Properties/.svn/entries create mode 100644 CommandEcho/Properties/.svn/prop-base/desktop.ini create mode 100644 CommandEcho/Properties/.svn/props/desktop.ini create mode 100644 CommandEcho/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 CommandEcho/Properties/.svn/text-base/desktop.ini create mode 100644 CommandEcho/Properties/.svn/tmp/desktop.ini create mode 100644 CommandEcho/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 CommandEcho/Properties/.svn/tmp/props/desktop.ini create mode 100644 CommandEcho/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 CommandEcho/Properties/AssemblyInfo.cs create mode 100644 CommandEcho/Properties/desktop.ini create mode 100644 CommandEcho/desktop.ini create mode 100644 GMCPEcho/.svn/all-wcprops create mode 100644 GMCPEcho/.svn/desktop.ini create mode 100644 GMCPEcho/.svn/dir-prop-base create mode 100644 GMCPEcho/.svn/entries create mode 100644 GMCPEcho/.svn/prop-base/desktop.ini create mode 100644 GMCPEcho/.svn/props/desktop.ini create mode 100644 GMCPEcho/.svn/text-base/GMCPEcho.cs.svn-base create mode 100644 GMCPEcho/.svn/text-base/GMCPEcho.csproj.svn-base create mode 100644 GMCPEcho/.svn/text-base/desktop.ini create mode 100644 GMCPEcho/.svn/tmp/desktop.ini create mode 100644 GMCPEcho/.svn/tmp/prop-base/desktop.ini create mode 100644 GMCPEcho/.svn/tmp/props/desktop.ini create mode 100644 GMCPEcho/.svn/tmp/text-base/desktop.ini create mode 100644 GMCPEcho/GMCPEcho.cs create mode 100644 GMCPEcho/GMCPEcho.csproj create mode 100644 GMCPEcho/Properties/.svn/all-wcprops create mode 100644 GMCPEcho/Properties/.svn/desktop.ini create mode 100644 GMCPEcho/Properties/.svn/dir-prop-base create mode 100644 GMCPEcho/Properties/.svn/entries create mode 100644 GMCPEcho/Properties/.svn/prop-base/desktop.ini create mode 100644 GMCPEcho/Properties/.svn/props/desktop.ini create mode 100644 GMCPEcho/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 GMCPEcho/Properties/.svn/text-base/desktop.ini create mode 100644 GMCPEcho/Properties/.svn/tmp/desktop.ini create mode 100644 GMCPEcho/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 GMCPEcho/Properties/.svn/tmp/props/desktop.ini create mode 100644 GMCPEcho/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 GMCPEcho/Properties/AssemblyInfo.cs create mode 100644 GMCPEcho/Properties/desktop.ini create mode 100644 GMCPEcho/desktop.ini create mode 100644 GQPredict/.svn/all-wcprops create mode 100644 GQPredict/.svn/desktop.ini create mode 100644 GQPredict/.svn/dir-prop-base create mode 100644 GQPredict/.svn/entries create mode 100644 GQPredict/.svn/prop-base/desktop.ini create mode 100644 GQPredict/.svn/props/desktop.ini create mode 100644 GQPredict/.svn/text-base/GQPredict.cs.svn-base create mode 100644 GQPredict/.svn/text-base/GQPredict.csproj.svn-base create mode 100644 GQPredict/.svn/text-base/desktop.ini create mode 100644 GQPredict/.svn/tmp/desktop.ini create mode 100644 GQPredict/.svn/tmp/prop-base/desktop.ini create mode 100644 GQPredict/.svn/tmp/props/desktop.ini create mode 100644 GQPredict/.svn/tmp/text-base/desktop.ini create mode 100644 GQPredict/GQPredict.cs create mode 100644 GQPredict/GQPredict.csproj create mode 100644 GQPredict/Properties/.svn/all-wcprops create mode 100644 GQPredict/Properties/.svn/desktop.ini create mode 100644 GQPredict/Properties/.svn/dir-prop-base create mode 100644 GQPredict/Properties/.svn/entries create mode 100644 GQPredict/Properties/.svn/prop-base/desktop.ini create mode 100644 GQPredict/Properties/.svn/props/desktop.ini create mode 100644 GQPredict/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 GQPredict/Properties/.svn/text-base/desktop.ini create mode 100644 GQPredict/Properties/.svn/tmp/desktop.ini create mode 100644 GQPredict/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 GQPredict/Properties/.svn/tmp/props/desktop.ini create mode 100644 GQPredict/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 GQPredict/Properties/AssemblyInfo.cs create mode 100644 GQPredict/Properties/desktop.ini create mode 100644 GQPredict/desktop.ini create mode 100644 Jayrock.Json.dll create mode 100644 Mapper/.svn/all-wcprops create mode 100644 Mapper/.svn/desktop.ini create mode 100644 Mapper/.svn/dir-prop-base create mode 100644 Mapper/.svn/entries create mode 100644 Mapper/.svn/prop-base/desktop.ini create mode 100644 Mapper/.svn/props/desktop.ini create mode 100644 Mapper/.svn/text-base/Area.cs.svn-base create mode 100644 Mapper/.svn/text-base/Edit.cs.svn-base create mode 100644 Mapper/.svn/text-base/Exit.cs.svn-base create mode 100644 Mapper/.svn/text-base/Mapper.cs.svn-base create mode 100644 Mapper/.svn/text-base/Mapper.csproj.svn-base create mode 100644 Mapper/.svn/text-base/Pathfind.cs.svn-base create mode 100644 Mapper/.svn/text-base/Room.cs.svn-base create mode 100644 Mapper/.svn/text-base/desktop.ini create mode 100644 Mapper/.svn/tmp/desktop.ini create mode 100644 Mapper/.svn/tmp/prop-base/desktop.ini create mode 100644 Mapper/.svn/tmp/props/desktop.ini create mode 100644 Mapper/.svn/tmp/text-base/desktop.ini create mode 100644 Mapper/Area.cs create mode 100644 Mapper/Edit.cs create mode 100644 Mapper/Exit.cs create mode 100644 Mapper/Finders/.svn/all-wcprops create mode 100644 Mapper/Finders/.svn/desktop.ini create mode 100644 Mapper/Finders/.svn/dir-prop-base create mode 100644 Mapper/Finders/.svn/entries create mode 100644 Mapper/Finders/.svn/prop-base/desktop.ini create mode 100644 Mapper/Finders/.svn/props/desktop.ini create mode 100644 Mapper/Finders/.svn/text-base/Area.cs.svn-base create mode 100644 Mapper/Finders/.svn/text-base/Entry.cs.svn-base create mode 100644 Mapper/Finders/.svn/text-base/Name.cs.svn-base create mode 100644 Mapper/Finders/.svn/text-base/Unmapped.cs.svn-base create mode 100644 Mapper/Finders/.svn/text-base/VisitAll.cs.svn-base create mode 100644 Mapper/Finders/.svn/text-base/desktop.ini create mode 100644 Mapper/Finders/.svn/tmp/desktop.ini create mode 100644 Mapper/Finders/.svn/tmp/prop-base/desktop.ini create mode 100644 Mapper/Finders/.svn/tmp/props/desktop.ini create mode 100644 Mapper/Finders/.svn/tmp/text-base/desktop.ini create mode 100644 Mapper/Finders/Area.cs create mode 100644 Mapper/Finders/Entry.cs create mode 100644 Mapper/Finders/Name.cs create mode 100644 Mapper/Finders/Unmapped.cs create mode 100644 Mapper/Finders/VisitAll.cs create mode 100644 Mapper/Finders/desktop.ini create mode 100644 Mapper/Mapper.cs create mode 100644 Mapper/Mapper.csproj create mode 100644 Mapper/Pathfind.cs create mode 100644 Mapper/Properties/.svn/all-wcprops create mode 100644 Mapper/Properties/.svn/desktop.ini create mode 100644 Mapper/Properties/.svn/dir-prop-base create mode 100644 Mapper/Properties/.svn/entries create mode 100644 Mapper/Properties/.svn/prop-base/desktop.ini create mode 100644 Mapper/Properties/.svn/props/desktop.ini create mode 100644 Mapper/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 Mapper/Properties/.svn/text-base/desktop.ini create mode 100644 Mapper/Properties/.svn/tmp/desktop.ini create mode 100644 Mapper/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 Mapper/Properties/.svn/tmp/props/desktop.ini create mode 100644 Mapper/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 Mapper/Properties/AssemblyInfo.cs create mode 100644 Mapper/Properties/desktop.ini create mode 100644 Mapper/Room.cs create mode 100644 Mapper/Scripting/.svn/all-wcprops create mode 100644 Mapper/Scripting/.svn/desktop.ini create mode 100644 Mapper/Scripting/.svn/dir-prop-base create mode 100644 Mapper/Scripting/.svn/entries create mode 100644 Mapper/Scripting/.svn/prop-base/desktop.ini create mode 100644 Mapper/Scripting/.svn/props/desktop.ini create mode 100644 Mapper/Scripting/.svn/text-base/AreaScript.cs.svn-base create mode 100644 Mapper/Scripting/.svn/text-base/desktop.ini create mode 100644 Mapper/Scripting/.svn/tmp/desktop.ini create mode 100644 Mapper/Scripting/.svn/tmp/prop-base/desktop.ini create mode 100644 Mapper/Scripting/.svn/tmp/props/desktop.ini create mode 100644 Mapper/Scripting/.svn/tmp/text-base/desktop.ini create mode 100644 Mapper/Scripting/AreaScript.cs create mode 100644 Mapper/Scripting/desktop.ini create mode 100644 Mapper/desktop.ini create mode 100644 MobDB/.svn/all-wcprops create mode 100644 MobDB/.svn/desktop.ini create mode 100644 MobDB/.svn/dir-prop-base create mode 100644 MobDB/.svn/entries create mode 100644 MobDB/.svn/prop-base/desktop.ini create mode 100644 MobDB/.svn/props/desktop.ini create mode 100644 MobDB/.svn/text-base/Mob.cs.svn-base create mode 100644 MobDB/.svn/text-base/MobDB.cs.svn-base create mode 100644 MobDB/.svn/text-base/MobDB.csproj.svn-base create mode 100644 MobDB/.svn/text-base/desktop.ini create mode 100644 MobDB/.svn/tmp/desktop.ini create mode 100644 MobDB/.svn/tmp/prop-base/desktop.ini create mode 100644 MobDB/.svn/tmp/props/desktop.ini create mode 100644 MobDB/.svn/tmp/text-base/desktop.ini create mode 100644 MobDB/Mob.cs create mode 100644 MobDB/MobDB.cs create mode 100644 MobDB/MobDB.csproj create mode 100644 MobDB/Properties/.svn/all-wcprops create mode 100644 MobDB/Properties/.svn/desktop.ini create mode 100644 MobDB/Properties/.svn/dir-prop-base create mode 100644 MobDB/Properties/.svn/entries create mode 100644 MobDB/Properties/.svn/prop-base/desktop.ini create mode 100644 MobDB/Properties/.svn/props/desktop.ini create mode 100644 MobDB/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 MobDB/Properties/.svn/text-base/desktop.ini create mode 100644 MobDB/Properties/.svn/tmp/desktop.ini create mode 100644 MobDB/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 MobDB/Properties/.svn/tmp/props/desktop.ini create mode 100644 MobDB/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 MobDB/Properties/AssemblyInfo.cs create mode 100644 MobDB/Properties/desktop.ini create mode 100644 MobDB/desktop.ini create mode 100644 MoonScript/.svn/all-wcprops create mode 100644 MoonScript/.svn/desktop.ini create mode 100644 MoonScript/.svn/dir-prop-base create mode 100644 MoonScript/.svn/entries create mode 100644 MoonScript/.svn/prop-base/desktop.ini create mode 100644 MoonScript/.svn/props/desktop.ini create mode 100644 MoonScript/.svn/text-base/MoonScript.cs.svn-base create mode 100644 MoonScript/.svn/text-base/MoonScript.csproj.svn-base create mode 100644 MoonScript/.svn/text-base/desktop.ini create mode 100644 MoonScript/.svn/tmp/desktop.ini create mode 100644 MoonScript/.svn/tmp/prop-base/desktop.ini create mode 100644 MoonScript/.svn/tmp/props/desktop.ini create mode 100644 MoonScript/.svn/tmp/text-base/desktop.ini create mode 100644 MoonScript/MoonScript.cs create mode 100644 MoonScript/MoonScript.csproj create mode 100644 MoonScript/Properties/.svn/all-wcprops create mode 100644 MoonScript/Properties/.svn/desktop.ini create mode 100644 MoonScript/Properties/.svn/dir-prop-base create mode 100644 MoonScript/Properties/.svn/entries create mode 100644 MoonScript/Properties/.svn/prop-base/desktop.ini create mode 100644 MoonScript/Properties/.svn/props/desktop.ini create mode 100644 MoonScript/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 MoonScript/Properties/.svn/text-base/desktop.ini create mode 100644 MoonScript/Properties/.svn/tmp/desktop.ini create mode 100644 MoonScript/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 MoonScript/Properties/.svn/tmp/props/desktop.ini create mode 100644 MoonScript/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 MoonScript/Properties/AssemblyInfo.cs create mode 100644 MoonScript/Properties/desktop.ini create mode 100644 MoonScript/desktop.ini create mode 100644 MudLog/.svn/all-wcprops create mode 100644 MudLog/.svn/desktop.ini create mode 100644 MudLog/.svn/dir-prop-base create mode 100644 MudLog/.svn/entries create mode 100644 MudLog/.svn/prop-base/desktop.ini create mode 100644 MudLog/.svn/props/desktop.ini create mode 100644 MudLog/.svn/text-base/MudLog.cs.svn-base create mode 100644 MudLog/.svn/text-base/MudLog.csproj.svn-base create mode 100644 MudLog/.svn/text-base/desktop.ini create mode 100644 MudLog/.svn/tmp/desktop.ini create mode 100644 MudLog/.svn/tmp/prop-base/desktop.ini create mode 100644 MudLog/.svn/tmp/props/desktop.ini create mode 100644 MudLog/.svn/tmp/text-base/desktop.ini create mode 100644 MudLog/MudLog.cs create mode 100644 MudLog/MudLog.csproj create mode 100644 MudLog/Properties/.svn/all-wcprops create mode 100644 MudLog/Properties/.svn/desktop.ini create mode 100644 MudLog/Properties/.svn/dir-prop-base create mode 100644 MudLog/Properties/.svn/entries create mode 100644 MudLog/Properties/.svn/prop-base/desktop.ini create mode 100644 MudLog/Properties/.svn/props/desktop.ini create mode 100644 MudLog/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 MudLog/Properties/.svn/text-base/desktop.ini create mode 100644 MudLog/Properties/.svn/tmp/desktop.ini create mode 100644 MudLog/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 MudLog/Properties/.svn/tmp/props/desktop.ini create mode 100644 MudLog/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 MudLog/Properties/AssemblyInfo.cs create mode 100644 MudLog/Properties/desktop.ini create mode 100644 MudLog/desktop.ini create mode 100644 MySQL/.svn/all-wcprops create mode 100644 MySQL/.svn/desktop.ini create mode 100644 MySQL/.svn/dir-prop-base create mode 100644 MySQL/.svn/entries create mode 100644 MySQL/.svn/prop-base/MySql.Data.Entity.dll.svn-base create mode 100644 MySQL/.svn/prop-base/MySql.Data.dll.svn-base create mode 100644 MySQL/.svn/prop-base/desktop.ini create mode 100644 MySQL/.svn/props/desktop.ini create mode 100644 MySQL/.svn/text-base/MySQL.cs.svn-base create mode 100644 MySQL/.svn/text-base/MySQL.csproj.svn-base create mode 100644 MySQL/.svn/text-base/MySql.Data.Entity.dll.svn-base create mode 100644 MySQL/.svn/text-base/MySql.Data.dll.svn-base create mode 100644 MySQL/.svn/text-base/desktop.ini create mode 100644 MySQL/.svn/tmp/desktop.ini create mode 100644 MySQL/.svn/tmp/prop-base/desktop.ini create mode 100644 MySQL/.svn/tmp/props/desktop.ini create mode 100644 MySQL/.svn/tmp/text-base/desktop.ini create mode 100644 MySQL/MySQL.cs create mode 100644 MySQL/MySQL.csproj create mode 100644 MySQL/MySql.Data.Entity.dll create mode 100644 MySQL/MySql.Data.dll create mode 100644 MySQL/Properties/.svn/all-wcprops create mode 100644 MySQL/Properties/.svn/desktop.ini create mode 100644 MySQL/Properties/.svn/dir-prop-base create mode 100644 MySQL/Properties/.svn/entries create mode 100644 MySQL/Properties/.svn/prop-base/desktop.ini create mode 100644 MySQL/Properties/.svn/props/desktop.ini create mode 100644 MySQL/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 MySQL/Properties/.svn/text-base/desktop.ini create mode 100644 MySQL/Properties/.svn/tmp/desktop.ini create mode 100644 MySQL/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 MySQL/Properties/.svn/tmp/props/desktop.ini create mode 100644 MySQL/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 MySQL/Properties/AssemblyInfo.cs create mode 100644 MySQL/Properties/desktop.ini create mode 100644 MySQL/desktop.ini create mode 100644 ProxyCore.dll create mode 100644 ProxyCore.xml create mode 100644 ProxyCore/.svn/all-wcprops create mode 100644 ProxyCore/.svn/desktop.ini create mode 100644 ProxyCore/.svn/dir-prop-base create mode 100644 ProxyCore/.svn/entries create mode 100644 ProxyCore/.svn/prop-base/desktop.ini create mode 100644 ProxyCore/.svn/props/desktop.ini create mode 100644 ProxyCore/.svn/text-base/Log.cs.svn-base create mode 100644 ProxyCore/.svn/text-base/ProxyCore.csproj.svn-base create mode 100644 ProxyCore/.svn/text-base/World.cs.svn-base create mode 100644 ProxyCore/.svn/text-base/desktop.ini create mode 100644 ProxyCore/.svn/tmp/desktop.ini create mode 100644 ProxyCore/.svn/tmp/prop-base/desktop.ini create mode 100644 ProxyCore/.svn/tmp/props/desktop.ini create mode 100644 ProxyCore/.svn/tmp/text-base/desktop.ini create mode 100644 ProxyCore/Input/.svn/all-wcprops create mode 100644 ProxyCore/Input/.svn/desktop.ini create mode 100644 ProxyCore/Input/.svn/dir-prop-base create mode 100644 ProxyCore/Input/.svn/entries create mode 100644 ProxyCore/Input/.svn/prop-base/desktop.ini create mode 100644 ProxyCore/Input/.svn/props/desktop.ini create mode 100644 ProxyCore/Input/.svn/text-base/InputData.cs.svn-base create mode 100644 ProxyCore/Input/.svn/text-base/InputEntry.cs.svn-base create mode 100644 ProxyCore/Input/.svn/text-base/InputHandler.cs.svn-base create mode 100644 ProxyCore/Input/.svn/text-base/desktop.ini create mode 100644 ProxyCore/Input/.svn/tmp/desktop.ini create mode 100644 ProxyCore/Input/.svn/tmp/prop-base/desktop.ini create mode 100644 ProxyCore/Input/.svn/tmp/props/desktop.ini create mode 100644 ProxyCore/Input/.svn/tmp/text-base/desktop.ini create mode 100644 ProxyCore/Input/InputData.cs create mode 100644 ProxyCore/Input/InputEntry.cs create mode 100644 ProxyCore/Input/InputHandler.cs create mode 100644 ProxyCore/Input/desktop.ini create mode 100644 ProxyCore/Log.cs create mode 100644 ProxyCore/Messages/.svn/all-wcprops create mode 100644 ProxyCore/Messages/.svn/desktop.ini create mode 100644 ProxyCore/Messages/.svn/dir-prop-base create mode 100644 ProxyCore/Messages/.svn/entries create mode 100644 ProxyCore/Messages/.svn/prop-base/desktop.ini create mode 100644 ProxyCore/Messages/.svn/props/desktop.ini create mode 100644 ProxyCore/Messages/.svn/text-base/Message.cs.svn-base create mode 100644 ProxyCore/Messages/.svn/text-base/desktop.ini create mode 100644 ProxyCore/Messages/.svn/tmp/desktop.ini create mode 100644 ProxyCore/Messages/.svn/tmp/prop-base/desktop.ini create mode 100644 ProxyCore/Messages/.svn/tmp/props/desktop.ini create mode 100644 ProxyCore/Messages/.svn/tmp/text-base/desktop.ini create mode 100644 ProxyCore/Messages/Message.cs create mode 100644 ProxyCore/Messages/desktop.ini create mode 100644 ProxyCore/Output/.svn/all-wcprops create mode 100644 ProxyCore/Output/.svn/desktop.ini create mode 100644 ProxyCore/Output/.svn/dir-prop-base create mode 100644 ProxyCore/Output/.svn/entries create mode 100644 ProxyCore/Output/.svn/prop-base/desktop.ini create mode 100644 ProxyCore/Output/.svn/props/desktop.ini create mode 100644 ProxyCore/Output/.svn/text-base/TriggerData.cs.svn-base create mode 100644 ProxyCore/Output/.svn/text-base/TriggerEntry.cs.svn-base create mode 100644 ProxyCore/Output/.svn/text-base/TriggerHandler.cs.svn-base create mode 100644 ProxyCore/Output/.svn/text-base/desktop.ini create mode 100644 ProxyCore/Output/.svn/tmp/desktop.ini create mode 100644 ProxyCore/Output/.svn/tmp/prop-base/desktop.ini create mode 100644 ProxyCore/Output/.svn/tmp/props/desktop.ini create mode 100644 ProxyCore/Output/.svn/tmp/text-base/desktop.ini create mode 100644 ProxyCore/Output/TriggerData.cs create mode 100644 ProxyCore/Output/TriggerEntry.cs create mode 100644 ProxyCore/Output/TriggerHandler.cs create mode 100644 ProxyCore/Output/desktop.ini create mode 100644 ProxyCore/Properties/.svn/all-wcprops create mode 100644 ProxyCore/Properties/.svn/desktop.ini create mode 100644 ProxyCore/Properties/.svn/dir-prop-base create mode 100644 ProxyCore/Properties/.svn/entries create mode 100644 ProxyCore/Properties/.svn/prop-base/desktop.ini create mode 100644 ProxyCore/Properties/.svn/props/desktop.ini create mode 100644 ProxyCore/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 ProxyCore/Properties/.svn/text-base/desktop.ini create mode 100644 ProxyCore/Properties/.svn/tmp/desktop.ini create mode 100644 ProxyCore/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 ProxyCore/Properties/.svn/tmp/props/desktop.ini create mode 100644 ProxyCore/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 ProxyCore/Properties/AssemblyInfo.cs create mode 100644 ProxyCore/Properties/desktop.ini create mode 100644 ProxyCore/ProxyCore.csproj create mode 100644 ProxyCore/Scripting/.svn/all-wcprops create mode 100644 ProxyCore/Scripting/.svn/desktop.ini create mode 100644 ProxyCore/Scripting/.svn/dir-prop-base create mode 100644 ProxyCore/Scripting/.svn/entries create mode 100644 ProxyCore/Scripting/.svn/prop-base/desktop.ini create mode 100644 ProxyCore/Scripting/.svn/props/desktop.ini create mode 100644 ProxyCore/Scripting/.svn/text-base/Plugin.cs.svn-base create mode 100644 ProxyCore/Scripting/.svn/text-base/PluginMgr.cs.svn-base create mode 100644 ProxyCore/Scripting/.svn/text-base/desktop.ini create mode 100644 ProxyCore/Scripting/.svn/tmp/desktop.ini create mode 100644 ProxyCore/Scripting/.svn/tmp/prop-base/desktop.ini create mode 100644 ProxyCore/Scripting/.svn/tmp/props/desktop.ini create mode 100644 ProxyCore/Scripting/.svn/tmp/text-base/desktop.ini create mode 100644 ProxyCore/Scripting/Plugin.cs create mode 100644 ProxyCore/Scripting/PluginMgr.cs create mode 100644 ProxyCore/Scripting/desktop.ini create mode 100644 ProxyCore/Utility/.svn/all-wcprops create mode 100644 ProxyCore/Utility/.svn/desktop.ini create mode 100644 ProxyCore/Utility/.svn/dir-prop-base create mode 100644 ProxyCore/Utility/.svn/entries create mode 100644 ProxyCore/Utility/.svn/prop-base/desktop.ini create mode 100644 ProxyCore/Utility/.svn/props/desktop.ini create mode 100644 ProxyCore/Utility/.svn/text-base/Colors.cs.svn-base create mode 100644 ProxyCore/Utility/.svn/text-base/Config.cs.svn-base create mode 100644 ProxyCore/Utility/.svn/text-base/JSON.cs.svn-base create mode 100644 ProxyCore/Utility/.svn/text-base/ServerConfig.cs.svn-base create mode 100644 ProxyCore/Utility/.svn/text-base/Wrap.cs.svn-base create mode 100644 ProxyCore/Utility/.svn/text-base/desktop.ini create mode 100644 ProxyCore/Utility/.svn/tmp/desktop.ini create mode 100644 ProxyCore/Utility/.svn/tmp/prop-base/desktop.ini create mode 100644 ProxyCore/Utility/.svn/tmp/props/desktop.ini create mode 100644 ProxyCore/Utility/.svn/tmp/text-base/desktop.ini create mode 100644 ProxyCore/Utility/Colors.cs create mode 100644 ProxyCore/Utility/Config.cs create mode 100644 ProxyCore/Utility/JSON.cs create mode 100644 ProxyCore/Utility/ServerConfig.cs create mode 100644 ProxyCore/Utility/Wrap.cs create mode 100644 ProxyCore/Utility/desktop.ini create mode 100644 ProxyCore/World.cs create mode 100644 ProxyCore/desktop.ini create mode 100644 ProxyMud.exe create mode 100644 ProxyMud.sln create mode 100644 ProxyMud/.svn/all-wcprops create mode 100644 ProxyMud/.svn/desktop.ini create mode 100644 ProxyMud/.svn/dir-prop-base create mode 100644 ProxyMud/.svn/entries create mode 100644 ProxyMud/.svn/prop-base/desktop.ini create mode 100644 ProxyMud/.svn/props/desktop.ini create mode 100644 ProxyMud/.svn/text-base/Program.cs.svn-base create mode 100644 ProxyMud/.svn/text-base/ProxyMud.csproj.svn-base create mode 100644 ProxyMud/.svn/text-base/desktop.ini create mode 100644 ProxyMud/.svn/tmp/desktop.ini create mode 100644 ProxyMud/.svn/tmp/prop-base/desktop.ini create mode 100644 ProxyMud/.svn/tmp/props/desktop.ini create mode 100644 ProxyMud/.svn/tmp/text-base/desktop.ini create mode 100644 ProxyMud/Network/.svn/all-wcprops create mode 100644 ProxyMud/Network/.svn/desktop.ini create mode 100644 ProxyMud/Network/.svn/dir-prop-base create mode 100644 ProxyMud/Network/.svn/entries create mode 100644 ProxyMud/Network/.svn/prop-base/desktop.ini create mode 100644 ProxyMud/Network/.svn/props/desktop.ini create mode 100644 ProxyMud/Network/.svn/text-base/NetworkAardwolf.cs.svn-base create mode 100644 ProxyMud/Network/.svn/text-base/NetworkBase.cs.svn-base create mode 100644 ProxyMud/Network/.svn/text-base/NetworkClient.cs.svn-base create mode 100644 ProxyMud/Network/.svn/text-base/NetworkServer.cs.svn-base create mode 100644 ProxyMud/Network/.svn/text-base/TelnetPacket.cs.svn-base create mode 100644 ProxyMud/Network/.svn/text-base/desktop.ini create mode 100644 ProxyMud/Network/.svn/tmp/desktop.ini create mode 100644 ProxyMud/Network/.svn/tmp/prop-base/desktop.ini create mode 100644 ProxyMud/Network/.svn/tmp/props/desktop.ini create mode 100644 ProxyMud/Network/.svn/tmp/text-base/desktop.ini create mode 100644 ProxyMud/Network/NetworkAardwolf.cs create mode 100644 ProxyMud/Network/NetworkBase.cs create mode 100644 ProxyMud/Network/NetworkClient.cs create mode 100644 ProxyMud/Network/NetworkServer.cs create mode 100644 ProxyMud/Network/TelnetPacket.cs create mode 100644 ProxyMud/Network/desktop.ini create mode 100644 ProxyMud/Program.cs create mode 100644 ProxyMud/Properties/.svn/all-wcprops create mode 100644 ProxyMud/Properties/.svn/desktop.ini create mode 100644 ProxyMud/Properties/.svn/dir-prop-base create mode 100644 ProxyMud/Properties/.svn/entries create mode 100644 ProxyMud/Properties/.svn/prop-base/desktop.ini create mode 100644 ProxyMud/Properties/.svn/props/desktop.ini create mode 100644 ProxyMud/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 ProxyMud/Properties/.svn/text-base/desktop.ini create mode 100644 ProxyMud/Properties/.svn/tmp/desktop.ini create mode 100644 ProxyMud/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 ProxyMud/Properties/.svn/tmp/props/desktop.ini create mode 100644 ProxyMud/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 ProxyMud/Properties/AssemblyInfo.cs create mode 100644 ProxyMud/Properties/desktop.ini create mode 100644 ProxyMud/ProxyMud.csproj create mode 100644 ProxyMud/desktop.ini create mode 100644 Resources/.svn/all-wcprops create mode 100644 Resources/.svn/desktop.ini create mode 100644 Resources/.svn/dir-prop-base create mode 100644 Resources/.svn/entries create mode 100644 Resources/.svn/prop-base/Jayrock.Json.dll.svn-base create mode 100644 Resources/.svn/prop-base/Jayrock.dll.svn-base create mode 100644 Resources/.svn/prop-base/desktop.ini create mode 100644 Resources/.svn/prop-base/zlib.dll.svn-base create mode 100644 Resources/.svn/props/desktop.ini create mode 100644 Resources/.svn/text-base/Jayrock.Json.dll.svn-base create mode 100644 Resources/.svn/text-base/Jayrock.dll.svn-base create mode 100644 Resources/.svn/text-base/desktop.ini create mode 100644 Resources/.svn/text-base/zlib.dll.svn-base create mode 100644 Resources/.svn/tmp/desktop.ini create mode 100644 Resources/.svn/tmp/prop-base/desktop.ini create mode 100644 Resources/.svn/tmp/props/desktop.ini create mode 100644 Resources/.svn/tmp/text-base/desktop.ini create mode 100644 Resources/Jayrock.Json.dll create mode 100644 Resources/Jayrock.dll create mode 100644 Resources/desktop.ini create mode 100644 Resources/zlib.dll create mode 100644 StayAlive/.svn/all-wcprops create mode 100644 StayAlive/.svn/desktop.ini create mode 100644 StayAlive/.svn/dir-prop-base create mode 100644 StayAlive/.svn/entries create mode 100644 StayAlive/.svn/prop-base/desktop.ini create mode 100644 StayAlive/.svn/props/desktop.ini create mode 100644 StayAlive/.svn/text-base/StayAlive.cs.svn-base create mode 100644 StayAlive/.svn/text-base/StayAlive.csproj.svn-base create mode 100644 StayAlive/.svn/text-base/desktop.ini create mode 100644 StayAlive/.svn/tmp/desktop.ini create mode 100644 StayAlive/.svn/tmp/prop-base/desktop.ini create mode 100644 StayAlive/.svn/tmp/props/desktop.ini create mode 100644 StayAlive/.svn/tmp/text-base/desktop.ini create mode 100644 StayAlive/Properties/.svn/all-wcprops create mode 100644 StayAlive/Properties/.svn/desktop.ini create mode 100644 StayAlive/Properties/.svn/dir-prop-base create mode 100644 StayAlive/Properties/.svn/entries create mode 100644 StayAlive/Properties/.svn/prop-base/desktop.ini create mode 100644 StayAlive/Properties/.svn/props/desktop.ini create mode 100644 StayAlive/Properties/.svn/text-base/AssemblyInfo.cs.svn-base create mode 100644 StayAlive/Properties/.svn/text-base/desktop.ini create mode 100644 StayAlive/Properties/.svn/tmp/desktop.ini create mode 100644 StayAlive/Properties/.svn/tmp/prop-base/desktop.ini create mode 100644 StayAlive/Properties/.svn/tmp/props/desktop.ini create mode 100644 StayAlive/Properties/.svn/tmp/text-base/desktop.ini create mode 100644 StayAlive/Properties/AssemblyInfo.cs create mode 100644 StayAlive/Properties/desktop.ini create mode 100644 StayAlive/StayAlive.cs create mode 100644 StayAlive/StayAlive.csproj create mode 100644 StayAlive/desktop.ini create mode 100644 desktop.ini create mode 100644 zlib.dll diff --git a/CPHelper/.svn/all-wcprops b/CPHelper/.svn/all-wcprops new file mode 100644 index 0000000..a547f8a --- /dev/null +++ b/CPHelper/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 31 +/svn/!svn/ver/55/trunk/CPHelper +END +CPHelper.csproj +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/30/trunk/CPHelper/CPHelper.csproj +END +CPHelper.cs +K 25 +svn:wc:ra_dav:version-url +V 43 +/svn/!svn/ver/55/trunk/CPHelper/CPHelper.cs +END diff --git a/CPHelper/.svn/desktop.ini b/CPHelper/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/.svn/dir-prop-base b/CPHelper/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/CPHelper/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/CPHelper/.svn/entries b/CPHelper/.svn/entries new file mode 100644 index 0000000..8066b76 --- /dev/null +++ b/CPHelper/.svn/entries @@ -0,0 +1,99 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/CPHelper +http://proxymud.googlecode.com/svn + + + +2012-02-03T09:23:01.115255Z +55 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +Properties +dir + +CPHelper.csproj +file + + + + +2016-03-25T22:18:42.968136Z +b6b9d8532fe49f254ea48003963156b0 +2012-01-23T07:49:14.623991Z +30 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +3161 + +CPHelper.cs +file + + + + +2016-03-25T22:18:42.968136Z +d4798a728907666a4b0eccfa26afc339 +2012-02-03T09:23:01.115255Z +55 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +48141 + diff --git a/CPHelper/.svn/prop-base/desktop.ini b/CPHelper/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/.svn/props/desktop.ini b/CPHelper/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/.svn/text-base/CPHelper.cs.svn-base b/CPHelper/.svn/text-base/CPHelper.cs.svn-base new file mode 100644 index 0000000..7429a2f --- /dev/null +++ b/CPHelper/.svn/text-base/CPHelper.cs.svn-base @@ -0,0 +1,1342 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Mapper; +using MobDB; +using ProxyCore; +using ProxyCore.Input; +using ProxyCore.Output; +using ProxyCore.Scripting; + +namespace CPHelper +{ + public class CPHelper : Plugin + { + public CPHelper() + : base("cphelper", "Campaign Helper") + { + Author = "Duckbat"; + Version = 5; + Description = "Matches mobs from database to rooms. Creates useful tags for campaign check."; + UpdateUrl = "www.duckbat.com/plugins/update.cphelper.txt"; + Website = "code.google.com/p/proxymud/"; + + Config = new CPHelperConfig(); + + RegisterCommand("autonoexp", @"^(\d+)", AutoNoexpCommand); + RegisterCommand("gowhere", "", GoWhereCommand, 3); + RegisterCommand("where", "(.+)", WhereCommand, 3); + RegisterCommand("gquest", "(.+)", GQCheckCommand, 2); + RegisterCommand("campaign", "(.+)", CampaignCheckCommand, 5); + RegisterCommand("cp", "(.+)", CampaignCheckCommand, 0); + RegisterCommand("awhere", "(.+)", AutoWhereCommand, 2); + RegisterCommand("ahunttrick", "(.+)", AutoHuntTrickCommand, 2); + RegisterCommand("hunt", "(.+)", HuntCommand, 2); + RegisterCommand("astop", "", AutoStopCommand, 2); + + RegisterTrigger("gq.quit", @"@wYou are no longer part of the current quest.", TriggerGQLevel2, TriggerFlags.NotRegex); + RegisterTrigger("hunt.target", @"@wYou seem unable to hunt that target for some reason.", TriggerHuntT, TriggerFlags.NotRegex); + RegisterTrigger("hunt.fail1", @"@wNo one in this area by that name.", TriggerHuntFail, TriggerFlags.NotRegex); + RegisterTrigger("hunt.fail2", @"^@wYou couldn't find a path to .+from here\.$", TriggerHuntNT); + RegisterTrigger("hunt.fail3", @"(.+?)is here!$", TriggerHuntFail); + RegisterTrigger("hunt.nottarget", @"^@wYou are confident that .+passed through here, heading \w+\.$", TriggerHuntNT); + RegisterTrigger("hunt.nottarget2", @"^@wYou have no idea what you're doing, but maybe .+left .+\?$", TriggerHuntNT); + RegisterTrigger("hunt.nottarget3", @"^@wYou are almost certain that .+is .+from here\.$", TriggerHuntNT); + RegisterTrigger("hunt.nottarget4", @"^@wThe trail of .+is confusing, but you're reasonable sure .+headed .+\.$", TriggerHuntNT); + RegisterTrigger("hunt.nottarget5", @"^@wThere are traces of .+having been here\. Perhaps they lead .+\?$", TriggerHuntNT); + RegisterTrigger("hunt.nottarget6", @"^@wYou are certain that .+is .+from here\.$", TriggerHuntNT); + RegisterTrigger("where.fail1", @"@wThere are too many doors and fences to see who is in this area.", TriggerWhereFail, TriggerFlags.NotRegex); + RegisterTrigger("where.fail2", @"^@wThere is no .+around here\.$", TriggerWhereFail); + RegisterTrigger("quest0", @"^\$gmcp\.comm\.quest\.action start$", TriggerQuest0); + RegisterTrigger("quest1", @"^\$gmcp\.comm\.quest\.room (.+)", TriggerQuest2); + RegisterTrigger("quest2", @"^\$gmcp\.comm\.quest\.area (.+)", TriggerQuest3); + RegisterTrigger("quest3", @"^\$gmcp\.comm\.quest\.targ (.+)", TriggerQuest1); + RegisterTrigger("enterroom", @"^\$gmcp\.room\.info\.num (-?\d+)$", TriggerRoomNum); + RegisterTrigger("check", @"^@wYou still have to kill (@c\d @w)?\* (.+?) (@w)?\((.+?)(@w)?\)$", TriggerCheck); + RegisterTrigger("char.level", @"^\$gmcp\.char\.status\.level (\d+)$", TriggerLevel); + RegisterTrigger("char.tnl", @"^\$gmcp\.char\.status\.tnl (\d+)$", TriggerTNL); + RegisterTrigger("request", @"^@.Commander Barcett tells you 'Good luck in your campaign!'$", TriggerRequest); + RegisterTrigger("cplevel", @"^@cLevel Taken\.\.\.\.\.\.\.\.: @g\[ \s+@w(\d+) @g\]$", TriggerCPLevel); + RegisterTrigger("gqlevel", @"^@RGlobal Quest@Y: @gGlobal quest # \d+ @whas been declared for levels @g(\d+) @wto @g(\d+)@w\.$", TriggerGQLevel); + RegisterTrigger("gqlevel2", @"^@RGlobal Quest@Y: @wThe global quest has been won by @Y\w+ @w- @Y\d+(st|th|rd|nd) @wwin\.$", TriggerGQLevel2); + RegisterTrigger("noexp1", @"@RYou will no longer receive experience. Happy questing!", NoexpTrigger1, TriggerFlags.NotRegex); + RegisterTrigger("noexp2", @"@wYou will now receive experience. Happy leveling!", NoexpTrigger2, TriggerFlags.NotRegex); + RegisterTrigger("cpcomplete", @"@GCONGRATULATIONS! @wYou have completed your campaign.", TriggerCPComplete, TriggerFlags.NotRegex); + RegisterTrigger("cpfail", "@wCampaign cleared.", TriggerCPComplete, TriggerFlags.NotRegex); + RegisterTrigger("scan1", "@w{scan}", TriggerScan1, TriggerFlags.NotRegex); + RegisterTrigger("scan0", "@w{/scan}", TriggerScan0, TriggerFlags.NotRegex); + RegisterTrigger("scan2", "^ @w- (.+)", TriggerScan2); + RegisterTrigger("scan.room", @"^@C(\d )?(North|East|West|South|Down|Up) from here you see:", TriggerScanRoom); + RegisterTrigger("scan.room2", @"^@CRight here you see:", TriggerScanRoom2); + RegisterTrigger("enemy", @"^$gmcp\.char\.status\.enemy(.*)", TriggerCurrentEnemy); + RegisterTrigger("cp.kill", @"@WCongratulations, that was one of your CAMPAIGN mobs!", TriggerOnKilled1, TriggerFlags.NotRegex); + RegisterTrigger("gq.kill", @"@RCongratulations, that was one of the GLOBAL QUEST mobs!", TriggerOnKilled0, TriggerFlags.NotRegex); + RegisterTrigger("room.area", @"^\$gmcp\.room\.info\.zone (.*)$", TriggerRoomInfoArea); + RegisterTrigger("where", @"^(.{28}) (.+)", TriggerWhere, TriggerFlags.NonAnsi); + } + + private uint CurrentRoomId = uint.MaxValue; + private bool AllowScan = false; + private int MyLvl = 0; + private int MyXP = 99999; + private int CampaignLvl = 0; + private int GQLvl = 0; + private long Spam = 0; + private bool IsNoexp = false; + private int AutoNoexp = 0; + private bool didEnter = false; + private readonly List[] Targets = new[] { new List(), new List() }; + private string CurEnemy = ""; + private string RoomInfoArea = ""; + private long WhereTimeout = 0; + private long HuntTimeout = 0; + private readonly List WhereRooms = new List(); + private uint FirstWhereRoom = uint.MaxValue; + private readonly List ScanRooms = new List(); + private uint CurScanRoom = uint.MaxValue; + private string QuestTarget = string.Empty; + private string QuestRoom = string.Empty; + private string QuestArea = string.Empty; + private bool QuestListen = false; + + private int AutoWhereNth = 1; + private string AutoWhereKey = string.Empty; + private int AutoHuntNth = 1; + private string AutoHuntKey = string.Empty; + + private bool TriggerHuntT(TriggerData t) + { + World.Instance.Execute("where " + AutoHuntNth + "." + AutoHuntKey, true); + AutoHuntKey = string.Empty; + AutoHuntNth = 1; + return false; + } + + private bool TriggerHuntNT(TriggerData t) + { + HuntTimeout = 0; + AutoHuntNth++; + return false; + } + + private bool TriggerWhereFail(TriggerData t) + { + AutoWhereKey = string.Empty; + AutoWhereNth = 1; + WhereTimeout = 0; + return false; + } + + private bool TriggerHuntFail(TriggerData t) + { + AutoHuntKey = string.Empty; + AutoHuntNth = 1; + HuntTimeout = 0; + return false; + } + + private bool AutoStopCommand(InputData i) + { + AutoWhereKey = string.Empty; + AutoHuntKey = string.Empty; + return true; + } + + private bool AutoWhereCommand(InputData i) + { + string arg; + if(!i.Arguments.Success || (arg = i.Arguments.Groups[1].Value.Trim()).Length == 0) + { + World.Instance.SendMessage("@wSyntax: awhere ", i.ClientMask); + return true; + } + + WhereTimeout = 0; + Match m = _argRegex.Match(arg); + if(m.Success) + { + AutoWhereKey = m.Groups[2].Value; + if(!int.TryParse(m.Groups[1].Value, out AutoWhereNth)) + AutoWhereNth = 1; + } + else + { + AutoWhereNth = 1; + AutoWhereKey = arg; + } + return true; + } + + private bool AutoHuntTrickCommand(InputData i) + { + string arg; + if(!i.Arguments.Success || (arg = i.Arguments.Groups[1].Value.Trim()).Length == 0) + { + World.Instance.SendMessage("@wSyntax: ahunttrick ", i.ClientMask); + return true; + } + + HuntTimeout = 0; + Match m = _argRegex.Match(arg); + if(m.Success) + { + AutoHuntKey = m.Groups[2].Value; + if(!int.TryParse(m.Groups[1].Value, out AutoHuntNth)) + AutoHuntNth = 1; + } + else + { + AutoHuntNth = 1; + AutoHuntKey = arg; + } + return true; + } + + private static Regex _argRegex = new Regex(@"^(\d+)\.(.+)", RegexOptions.Compiled); + + private bool TriggerQuest0(TriggerData t) + { + QuestListen = true; + return false; + } + + private bool TriggerQuest1(TriggerData t) + { + if(!QuestListen) + return false; + + QuestTarget = MobDB.MobDB.NormalizeName(t.Match.Groups[1].Value.Trim()); + return false; + } + + private bool TriggerQuest2(TriggerData t) + { + if(!QuestListen) + return false; + + QuestRoom = t.Match.Groups[1].Value.Trim(); + return false; + } + + private bool TriggerQuest3(TriggerData t) + { + if(!QuestListen) + return false; + + QuestListen = false; + QuestArea = t.Match.Groups[1].Value.Trim(); + + int minLevel = MyLvl - 8; + int maxLevel = MyLvl + 10; + if(MyLvl >= 200) + maxLevel = MyLvl + 20; + + uint bestMobRoom = uint.MaxValue; + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + List Areas = new List(); + List Rooms = new List(); + foreach(Area x in map.Areas) + { + if(x.Name == QuestArea) + Areas.Add(x.Keyword); + } + foreach(Room r in map.Rooms) + { + if(r.Area.Name != QuestArea) + continue; + + if(r.Name != QuestRoom) + continue; + + Rooms.Add(r.Entry); + } + foreach(Mob m in db.Mobs) + { + if(m.Level < minLevel || m.Level > maxLevel) + continue; + + if(!m.Name.Contains(QuestTarget)) + continue; + + if(!m.Areas.Contains("all")) + { + bool f = false; + foreach(string x in m.Areas) + { + if(Areas.Contains(x)) + { + f = true; + break; + } + } + + if(!f) + continue; + } + + bestMobRoom = m.GetBestRoom(); + if(bestMobRoom == uint.MaxValue) + continue; + + Room br = map.GetRoom(bestMobRoom); + if(br != null) + { + Pathfinder_Mob pf = new Pathfinder_Mob(m, Rooms.ToArray()); + pf.StartRooms = new[] { br }; + PathfindResult pr = map.Get(pf); + if(!pr.Success || pr.Target == null) + { + bestMobRoom = uint.MaxValue; + continue; + } + + bestMobRoom = pr.Target.Entry; + break; + } + } + + + if(Rooms.Count != 0) + { + WhereRooms.Clear(); + WhereRooms.AddRange(Rooms); + FirstWhereRoom = bestMobRoom; + World.Instance.SendMessage("@wAdded @C" + Rooms.Count + " @wroom" + (Rooms.Count != 1 ? "s" : "") + " to @G\"where\" @wlist. Use '@Wgowhere@w' to visit them."); + } + + return false; + } + + private bool TriggerScanRoom(TriggerData t) + { + CurScanRoom = uint.MaxValue; + int c = 1; + if(t.Match.Groups[1].Length != 0) + int.TryParse(t.Match.Groups[1].Value, out c); + char d = t.Match.Groups[2].Value.ToLower()[0]; + + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(map == null) + return false; + + Room r = map.GetRoom(CurrentRoomId); + if(r == null) + return false; + + for(int i = 0; i < c; i++) + { + Exit e = r.GetExit(d); + if(e == null) + return false; + + r = e.To; + } + + if(string.IsNullOrEmpty(r.Name)) + return false; + + CurScanRoom = r.Entry; + return false; + } + + private bool TriggerScanRoom2(TriggerData t) + { + CurScanRoom = CurrentRoomId; + return false; + } + + private bool GQCheckCommand(InputData i) + { + if(i.Arguments.Success && i.Arguments.Groups[1].Length <= 5 && + "check".StartsWith(i.Arguments.Groups[1].Value.ToLower())) + Targets[0].Clear(); + return false; + } + + private bool CampaignCheckCommand(InputData i) + { + if(i.Arguments.Success && i.Arguments.Groups[1].Length <= 5 && + "check".StartsWith(i.Arguments.Groups[1].Value.ToLower())) + Targets[1].Clear(); + return false; + } + + private bool TriggerRoomNum(TriggerData t) + { + uint u; + if(uint.TryParse(t.Match.Groups[1].Value, out u)) + { + CurrentRoomId = u; + if(FirstWhereRoom == u) + FirstWhereRoom = uint.MaxValue; + if(WhereRooms.Remove(u)) + World.Instance.SendMessage("@wEntered a @G\"where\" @wroom. Now have @C" + WhereRooms.Count + " @wroom" + (WhereRooms.Count != 1 ? "s" : "") + " remaining. Use '@Wgowhere@w' to go to the next one."); + } + else + CurrentRoomId = uint.MaxValue; + return false; + } + + private bool TriggerWhere(TriggerData t) + { + if(WhereTimeout < World.Instance.MSTime) + return false; + + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(map == null) + return false; + + string rName = t.Match.Groups[2].Value.Trim(); + + List r = new List(); + foreach(Room x in map.Rooms) + { + if(x.Area.Keyword == RoomInfoArea && x.Name == rName) + r.Add(x); + } + + string mobName = t.Match.Groups[1].Value.Trim(); + mobName = MobDB.MobDB.NormalizeName(mobName); + + if(!string.IsNullOrEmpty(AutoWhereKey)) + { + if(Targets[0].Count != 0) + { + foreach(CPEntry x in Targets[0]) + { + if(x.Name.StartsWith(mobName)) + { + AutoWhereKey = string.Empty; + break; + } + } + } + else + { + foreach(CPEntry x in Targets[1]) + { + if(x.Name.StartsWith(mobName)) + { + AutoWhereKey = string.Empty; + break; + } + } + + if(QuestTarget.StartsWith(mobName)) + AutoWhereKey = string.Empty; + } + } + + if(r.Count == 0) + return false; + + WhereRooms.Clear(); + FirstWhereRoom = uint.MaxValue; + WhereTimeout = 0; + foreach(Room x in r) + { + if(CurrentRoomId != x.Entry) + WhereRooms.Add(x.Entry); + } + + if(WhereRooms.Count == 0) + return false; + + World.Instance.SendMessage("@wAdded @C" + WhereRooms.Count + " @wroom" + (WhereRooms.Count != 1 ? "s" : "") + " to @G\"where\"@w. Type '@Wgowhere@w' to go there."); + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + return false; + + foreach(Mob m in db.Mobs) + { + if(!m.Areas.Contains("all") && !m.Areas.Contains(RoomInfoArea)) + continue; + + bool f = false; + foreach(string z in m.Name) + { + if(mobName.Length >= 26) + { + if(z.StartsWith(mobName)) + { + f = true; + break; + } + } + else if(z == mobName) + { + f = true; + break; + } + } + + if(!f) + continue; + + uint b = m.GetBestRoom(); + if(b == uint.MaxValue || b == CurrentRoomId) + continue; + + Room br = map.GetRoom(b); + if(br == null || br.Entry == CurrentRoomId) + continue; + + if(WhereRooms.Contains(b)) + FirstWhereRoom = b; + else + { + Pathfinder_Mob pf = new Pathfinder_Mob(m, WhereRooms.ToArray()); + pf.StartRooms = new[] { br }; + + PathfindResult pr = map.Get(pf); + if(pr.Success && pr.Target != null) + FirstWhereRoom = pr.Target.Entry; + } + + break; + } + + return false; + } + + private bool WhereCommand(InputData i) + { + if(i.Arguments.Success && i.Arguments.Groups[1].Value.Trim().Length > 0) + WhereTimeout = World.Instance.MSTime + 6000; + return false; + } + + private bool HuntCommand(InputData i) + { + if(i.Arguments.Success && i.Arguments.Groups[1].Value.Trim().Length > 0) + HuntTimeout = World.Instance.MSTime + 6000; + return false; + } + + private bool GoWhereCommand(InputData i) + { + if(WhereRooms.Count == 0) + World.Instance.SendMessage("@wThere are no where rooms available.", i.ClientMask); + else + { + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(map == null) + { + World.Instance.SendMessage("@wInternal error.", i.ClientMask); + return true; + } + + Pathfinder_Entry pf = new Pathfinder_Entry(FirstWhereRoom != uint.MaxValue ? new[] { FirstWhereRoom } : WhereRooms.ToArray()); + map.FillPathFinder(pf); + PathfindResult pr = map.Get(pf); + if(!pr.Success) + World.Instance.SendMessage("@wCouldn't find a path to any of the where rooms.", i.ClientMask); + else + map.Goto(pr); + } + return true; + } + + public override void OnLogin() + { + base.OnLogin(); + + RegisterCommand("goto", @"(.+)", GotoMobCommand, 2, CMDFlags.None, "mobdb"); + } + + private bool GotoMobCommand(InputData i) + { + if(string.IsNullOrEmpty(RoomInfoArea)) + { + World.Instance.SendMessage("@wDon't know yet which area we are in. Type look to find out.", i.ClientMask); + return true; + } + + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: mobdb goto ", i.ClientMask); + return true; + } + + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + { + World.Instance.SendMessage("@wInternal error.", i.ClientMask); + return true; + } + + List toGoRooms = new List(); + string name = i.Arguments.Groups[1].Value.ToLower(); + foreach(Mob m in db.Mobs) + { + if(!m.Areas.Contains("all") && !m.Areas.Contains(RoomInfoArea)) + continue; + + uint p = m.GetBestRoom(); + if(p == uint.MaxValue) + continue; + + foreach(string z in m.Name) + { + if(z.ToLower().Contains(name)) + { + if(!toGoRooms.Contains(p)) + toGoRooms.Add(p); + break; + } + } + } + + if(toGoRooms.Count == 0) + { + World.Instance.SendMessage("@wFound no mobs with that name in current area.", i.ClientMask); + return true; + } + + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(map == null) + { + World.Instance.SendMessage("@wInternal error.", i.ClientMask); + return true; + } + + Pathfinder_Entry pf = new Pathfinder_Entry(toGoRooms.ToArray()); + if(!map.FillPathFinder(pf)) + { + World.Instance.SendMessage("@wWe are in an unknown room.", i.ClientMask); + return true; + } + + PathfindResult pr = map.Get(pf); + if(!pr.Success) + { + World.Instance.SendMessage("@wCouldn't find a path to any of the mobs with that name.", i.ClientMask); + return true; + } + + map.Goto(pr); + return true; + } + + private bool TriggerRoomInfoArea(TriggerData t) + { + RoomInfoArea = t.Match.Groups[1].Value.Trim(); + return false; + } + + private bool TriggerOnKilled0(TriggerData t) + { + if(string.IsNullOrEmpty(CurEnemy)) + return false; + + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + return false; + + CurEnemy = MobDB.MobDB.NormalizeName(CurEnemy); + + for(int i = 0; i < Targets[0].Count; i++) + { + CPEntry x = Targets[0][i]; + + if(x.Name != CurEnemy) + continue; + + foreach(uint u in x.Mobs) + { + Mob m = db.GetMob(u); + if(m == null) + continue; + + if(!m.Areas.Contains("all") && !m.Areas.Contains(RoomInfoArea)) + continue; + + if(m.Name.Contains(CurEnemy)) + { + if(x.Count > 1) + { + x.Count--; + return false; + } + + Targets[0].RemoveAt(i); + UpdateColors(); + return false; + } + } + } + + return false; + } + + private bool TriggerOnKilled1(TriggerData t) + { + if(string.IsNullOrEmpty(CurEnemy)) + return false; + + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + return false; + + CurEnemy = MobDB.MobDB.NormalizeName(CurEnemy); + + for(int i = 0; i < Targets[1].Count; i++) + { + CPEntry x = Targets[1][i]; + + if(x.Name != CurEnemy) + continue; + + foreach(uint u in x.Mobs) + { + Mob m = db.GetMob(u); + if(m == null) + continue; + + if(!m.Areas.Contains("all") && !m.Areas.Contains(RoomInfoArea)) + continue; + + if(m.Name.Contains(CurEnemy)) + { + if(x.Count > 1) + { + x.Count--; + return false; + } + + Targets[1].RemoveAt(i); + UpdateColors(); + return false; + } + } + } + + return false; + } + + private bool TriggerCurrentEnemy(TriggerData t) + { + CurEnemy = Colors.RemoveColors(t.Match.Groups[1].Value, false).Trim(); + return false; + } + + private bool TriggerScan0(TriggerData t) + { + AllowScan = false; + if(Config.GetInt32("Tags.Scan.Gag", 1) != 0) + t.Msg.AuthMask = 0; + + StringBuilder strScan = new StringBuilder(); + foreach(uint u in ScanRooms) + { + if(strScan.Length > 0) + strScan.Append(" "); + strScan.Append(u); + } + World.Instance.SendMessage("@w{scan_rooms}" + strScan); + return false; + } + + private bool TriggerScan1(TriggerData t) + { + ScanRooms.Clear(); + AllowScan = true; + if(Config.GetInt32("Tags.Scan.Gag", 1) != 0) + t.Msg.AuthMask = 0; + return false; + } + + private bool TriggerScan2(TriggerData td) + { + if(!AllowScan) + return false; + + if(Targets[0].Count == 0 && Targets[1].Count == 0) + return false; + + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + return false; + + string t = Colors.RemoveDuplicateColors("@w" + td.Match.Groups[1].Value); + if(t.StartsWith("@R(Wounded)")) + t = Colors.RemoveDuplicateColors("@R" + t.Substring("@R(Wounded)".Length)).Trim(); + + if(t.StartsWith("@w(Invis)")) + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(Invis)".Length)).Trim(); + else if(t.StartsWith("@w(I)")) + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(I)".Length)).Trim(); + + if(t.StartsWith("@w(Hidden)")) + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(Hidden)".Length)).Trim(); + else if(t.StartsWith("@w(H)")) + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(H)".Length)).Trim(); + + if(t.StartsWith("@W(Translucent)")) + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(Translucent)".Length)).Trim(); + else if(t.StartsWith("@W(T)")) + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(T)".Length)).Trim(); + + if(t.StartsWith("@C(Charmed) ")) + return false; + else if(t.StartsWith("@C(C)")) + return false; + + if(t.StartsWith("@M(Animated) ")) + return false; + else if(t.StartsWith("@M(A)")) + return false; + + if(t.StartsWith("@B(Diseased)")) + t = Colors.RemoveDuplicateColors("@B" + t.Substring("@B(Diseased)".Length)).Trim(); + else if(t.StartsWith("@B(D)")) + t = Colors.RemoveDuplicateColors("@B" + t.Substring("@B(D)".Length)).Trim(); + + if(t.StartsWith("@D(Marked)")) + t = Colors.RemoveDuplicateColors("@D" + t.Substring("@D(Marked)".Length)).Trim(); + else if(t.StartsWith("@D(X)")) + t = Colors.RemoveDuplicateColors("@D" + t.Substring("@D(X)".Length)).Trim(); + + if(t.StartsWith("@r(Red Aura)")) + t = Colors.RemoveDuplicateColors("@r" + t.Substring("@r(Red Aura)".Length)).Trim(); + else if(t.StartsWith("@r(R)")) + t = Colors.RemoveDuplicateColors("@r" + t.Substring("@r(R)".Length)).Trim(); + else if(t.StartsWith("@y(Golden Aura)")) + t = Colors.RemoveDuplicateColors("@y" + t.Substring("@y(Golden Aura)".Length)).Trim(); + else if(t.StartsWith("@y(G)")) + t = Colors.RemoveDuplicateColors("@y" + t.Substring("@y(G)".Length)).Trim(); + + if(t.StartsWith("@W(White Aura)")) + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(White Aura)".Length)).Trim(); + else if(t.StartsWith("@W(W)")) + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(W)".Length)).Trim(); + + if(t.StartsWith("@R(Angry)")) + t = Colors.RemoveDuplicateColors("@R" + t.Substring("@R(Angry)".Length)).Trim(); + + if(t.StartsWith("@w(Linkdead)")) + return false; + + if(t.StartsWith("@R(RAIDER)")) + return false; + + if(t.StartsWith("@R(TRAITOR)")) + return false; + + if(t.StartsWith("@G(DEFENDER)")) + return false; + + if(t.StartsWith("@R(WANTED)")) + return false; + + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(map == null) + return false; + + string n = Colors.RemoveColors(t, false).ToLower().Trim(); + if(t.StartsWith("@") && !t.StartsWith("@@") && t.Length >= 2) + t = t.Substring(2); + if(Targets[0].Count != 0) + { + foreach(CPEntry x in Targets[0]) + { + if(x.Name.ToLower() != n) + continue; + + int ind = td.Msg.Msg.LastIndexOf(t); + if(ind != -1) + { + td.Msg.Msg = td.Msg.Msg.Remove(ind); + td.Msg.Msg = td.Msg.Msg.Insert(ind, Config.GetString("Color.Scan", "@R") + x.Name); + } + + if(CurScanRoom != uint.MaxValue && !ScanRooms.Contains(CurScanRoom)) + ScanRooms.Add(CurScanRoom); + + return false; + } + } + else + { + foreach(CPEntry x in Targets[1]) + { + if(x.Name.ToLower() == n) + { + int ind = td.Msg.Msg.LastIndexOf(t); + if(ind != -1) + { + td.Msg.Msg = td.Msg.Msg.Remove(ind); + td.Msg.Msg = td.Msg.Msg.Insert(ind, Config.GetString("Color.Scan", "@R") + x.Name); + } + + if(CurScanRoom != uint.MaxValue && !ScanRooms.Contains(CurScanRoom)) + ScanRooms.Add(CurScanRoom); + + return false; + } + } + } + + return false; + } + + private bool TriggerCPComplete(TriggerData t) + { + Targets[1].Clear(); + UpdateColors(); + return false; + } + + private bool NoexpTrigger1(TriggerData t) + { + IsNoexp = true; + didEnter = false; + return false; + } + + private bool NoexpTrigger2(TriggerData t) + { + IsNoexp = false; + didEnter = false; + return false; + } + + public override void Update(long msTime) + { + base.Update(msTime); + + if(!string.IsNullOrEmpty(AutoWhereKey)) + { + if(WhereTimeout > msTime) + return; + if(WhereTimeout != 0) + AutoWhereKey = string.Empty; + else + { + World.Instance.Execute("where " + AutoWhereNth + "." + AutoWhereKey, true); + AutoWhereNth++; + return; + } + } + + if(!string.IsNullOrEmpty(AutoHuntKey)) + { + if(HuntTimeout > msTime) + return; + if(HuntTimeout != 0) + AutoHuntKey = string.Empty; + else + { + World.Instance.Execute("hunt " + AutoHuntNth + "." + AutoHuntKey, true); + return; + } + } + + if(didEnter) + return; + + if(AutoNoexp == 0) + return; + + bool shouldNoExp = MyLvl > CampaignLvl && MyXP <= AutoNoexp; + if(shouldNoExp) + { + if(IsNoexp) + return; + } + else + { + if(!IsNoexp) + return; + } + + World.Instance.Execute("noexp", true); + didEnter = true; + } + + private bool AutoNoexpCommand(InputData t) + { + int n; + if(!t.Arguments.Success || !int.TryParse(t.Arguments.Groups[1].Value, out n)) + { + World.Instance.SendMessage("@wSyntax: autonoexp ", t.ClientMask); + return true; + } + + if(n <= 0) + { + AutoNoexp = 0; + World.Instance.SendMessage("@wYou will no longer turn noexp on automatically.", t.ClientMask); + return true; + } + + AutoNoexp = n; + World.Instance.SendMessage("@wYou will now turn noexp on when you can ask a campaign and XP goes to @C" + n + " @wor below.", t.ClientMask); + World.Instance.SendMessage("@wUse '@Wautonoexp 0@w' to turn this off.", t.ClientMask); + return true; + } + + private bool TriggerGQLevel(TriggerData t) + { + Targets[0].Clear(); + int lvl; + if(int.TryParse(t.Match.Groups[1].Value, out lvl)) + GQLvl = lvl; + UpdateColors(); + return false; + } + + private bool TriggerGQLevel2(TriggerData t) + { + Targets[0].Clear(); + GQLvl = 0; + UpdateColors(); + return false; + } + + private bool TriggerCPLevel(TriggerData t) + { + int lvl; + if(int.TryParse(t.Match.Groups[1].Value, out lvl)) + CampaignLvl = lvl; + return false; + } + + private bool TriggerRequest(TriggerData t) + { + CampaignLvl = MyLvl; + return false; + } + + private bool TriggerLevel(TriggerData t) + { + int lvl; + if(int.TryParse(t.Match.Groups[1].Value, out lvl)) + MyLvl = lvl; + return false; + } + + private bool TriggerTNL(TriggerData t) + { + int lvl; + if(int.TryParse(t.Match.Groups[1].Value, out lvl)) + MyXP = lvl; + return false; + } + + private bool TriggerCheck(TriggerData t) + { + string mobName = MobDB.MobDB.NormalizeName(Colors.RemoveColors(t.Match.Groups[2].Value, false).Trim()); + string placeName = Colors.RemoveColors(t.Match.Groups[4].Value, false).Trim(); + if(placeName.EndsWith(" - Dead")) + placeName = placeName.Substring(0, placeName.LastIndexOf(" - Dead")); + + if(Config.GetInt32("Tags.On", 1) != 0) + { + World.Instance.SendMessage("@w{cp_mob}" + mobName); + World.Instance.SendMessage("@w{cp_place}" + placeName); + } + + if(CampaignLvl == 0 && t.Match.Groups[1].Length == 0) + { + if(Spam < World.Instance.MSTime) + { + Spam = World.Instance.MSTime + 300; + World.Instance.SendMessage("@WPlease see '@Rcampaign info@W' first! I need the level to check for mobs."); + } + return false; + } + + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(db == null || map == null) + return false; + + int minLevel = CampaignLvl - 5; + int maxLevel = CampaignLvl + 12; + if(CampaignLvl >= 200) + maxLevel = CampaignLvl + 20; + + if(t.Match.Groups[1].Length != 0) + { + minLevel = MyLvl - 20; + maxLevel = MyLvl + 10; + if(GQLvl != 0) + { + minLevel = GQLvl - 6; + maxLevel = GQLvl + 15; + if(GQLvl >= 186) + maxLevel = GQLvl + 21; + } + } + + List Rooms = new List(); + List Areas = new List(); + foreach(Area z in map.Areas) + { + if(z.Name == placeName) + Areas.Add(z.Keyword); + } + + CPEntry e = new CPEntry(); + e.Name = mobName; + e.PlaceName = placeName; + + if(Areas.Count != 0) + { + // Area CP / GQ? + foreach(string a in Areas) + { + foreach(Mob x in db.Mobs) + { + if(x.Level < minLevel || x.Level > maxLevel) + continue; + if(!x.Areas.Contains("all") && !x.Areas.Contains(a)) + continue; + + if(!x.Name.Contains(mobName)) + continue; + + uint roomId = x.GetBestRoom(); + if(roomId == uint.MaxValue) + continue; + + Rooms.Add(roomId); + if(!e.Mobs.Contains(x.Entry)) + e.Mobs.Add(x.Entry); + } + } + } + else + { + List r = new List(); + List p = new List(); + foreach(Room x in map.Rooms) + { + if(x.Name == placeName) + { + r.Add(x.Entry); + if(!p.Contains(x.Area.Keyword)) + p.Add(x.Area.Keyword); + } + } + + foreach(Mob x in db.Mobs) + { + if(x.Level < minLevel || x.Level > maxLevel) + continue; + if(!x.Areas.Contains("all")) + { + bool f = false; + foreach(string z in x.Areas) + { + if(p.Contains(z)) + { + f = true; + break; + } + } + + if(!f) + continue; + } + + if(!x.Name.Contains(mobName)) + continue; + + if(!e.Mobs.Contains(x.Entry)) + e.Mobs.Add(x.Entry); + + uint u = x.GetBestRoom(); + if(u == uint.MaxValue) + continue; + + Room br = map.GetRoom(u); + if(br == null) + continue; + + Pathfinder_Mob pf = new Pathfinder_Mob(x, r.ToArray()); + pf.StartRooms = new[] { br }; + PathfindResult pr = map.Get(pf); + if(pr.Success && pr.Target != null) + u = pr.Target.Entry; + + if(!Rooms.Contains(u)) + Rooms.Add(u); + } + } + + if(t.Match.Groups[1].Length == 0) + e.Count = 1; + else + { + int n; + if(int.TryParse(Colors.RemoveColors(t.Match.Groups[1].Value, false).Trim(), out n)) + { + e.Count = n; + e.IsGQ = true; + } + else + { + e.Count = 1; + e.IsGQ = true; + } + } + + if(e.IsGQ) + Targets[0].Add(e); + else + Targets[1].Add(e); + + StringBuilder strRooms = new StringBuilder(); + foreach(uint u in Rooms) + { + if(strRooms.Length > 0) + strRooms.Append(" "); + strRooms.Append(u); + } + World.Instance.SendMessage("@w{cp_room}" + strRooms); + + UpdateColors(); + return false; + } + + private void UpdateColors() + { + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + return; + + db.ClearMobColors(); + if(string.IsNullOrEmpty(Config.GetString("Color.Room", "@C"))) + return; + + if(Targets[0].Count != 0) + { + foreach(CPEntry x in Targets[0]) + { + foreach(uint u in x.Mobs) + db.SetMobColor(u, Config.GetString("Color.Room", "@C")); + } + } + else + { + foreach(CPEntry x in Targets[1]) + { + foreach(uint u in x.Mobs) + db.SetMobColor(u, Config.GetString("Color.Room", "@C")); + } + } + } + + private CPEntry IsSame(List entries, string mobName, string placeName) + { + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(map == null || db == null) + return null; + + List areas = new List(); + foreach(Area x in map.Areas) + { + if(x.Name == placeName) + areas.Add(x.Keyword); + } + if(areas.Count == 0) + { + foreach(Room x in map.Rooms) + { + if(x.Name == placeName && !areas.Contains(x.Area.Keyword)) + areas.Add(x.Area.Keyword); + } + } + + foreach(CPEntry x in entries) + { + if(x.Name != mobName) + continue; + + foreach(uint u in x.Mobs) + { + Mob m = db.GetMob(u); + if(m == null) + continue; + + if(!m.Areas.Contains("all")) + { + bool f = false; + foreach(string y in m.Areas) + { + if(areas.Contains(y)) + { + f = true; + break; + } + } + + if(!f) + continue; + } + + return x; + } + } + + return null; + } + } + + public class Pathfinder_Mob : Pathfinder_Entry + { + public Pathfinder_Mob(Mob m, params uint[] roomId) + : base(roomId) + { + mob = m; + CanUsePortals = false; + CanUseRecalls = false; + CharacterLevel = m.Level; + CharacterTier = 0; + IsGlobalQuest = false; + IsSingleClassTier0 = false; + } + + private Mob mob; + private static string[] Normals = new[] { "n", "e", "s", "w", "u", "d" }; + + public override bool CanUseExit(Exit e) + { + if(!Normals.Contains(e.Command)) + return false; + + if(!mob.Areas.Contains("all") && !mob.Areas.Contains(e.To.Area.Keyword)) + return false; + + return base.CanUseExit(e); + } + } + + public class CPHelperConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Tags.On", 1, "Display tags on campaign / gq check messages for easier capture. If you turn tags off then room IDs will still be displayed if we find a mob in database."); + CreateSetting("Tags.Scan.Gag", 1, "Gag {scan} tags."); + CreateSetting("Color.Room", "@C", "Color campaign / gq mobs differently (the $mob.color value) when we have a CP / GQ mob target. Enter empty value to skip this. Mob has to be in database for this to work."); + CreateSetting("Color.Scan", "@R", "Color campaign / gq mobs differently when scanning. Enter empty value to skip this. You need to have \"tags scan on\" for this to work. Mob has to be in database for this to work."); + } + } + + internal class CPEntry + { + internal bool IsGQ = false; + internal string Name; + internal int Count; + internal string PlaceName; + internal List Mobs = new List(); + } +} diff --git a/CPHelper/.svn/text-base/CPHelper.csproj.svn-base b/CPHelper/.svn/text-base/CPHelper.csproj.svn-base new file mode 100644 index 0000000..7f0290f --- /dev/null +++ b/CPHelper/.svn/text-base/CPHelper.csproj.svn-base @@ -0,0 +1,71 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {11D11063-A123-4F02-9C20-FB76C49A4B52} + Library + Properties + CPHelper + CPHelper + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Mapper\bin\Release\Mapper.dll + + + False + ..\MobDB\bin\Release\MobDB.dll + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/CPHelper/.svn/text-base/desktop.ini b/CPHelper/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/.svn/tmp/desktop.ini b/CPHelper/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/.svn/tmp/prop-base/desktop.ini b/CPHelper/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/.svn/tmp/props/desktop.ini b/CPHelper/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/.svn/tmp/text-base/desktop.ini b/CPHelper/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/CPHelper.cs b/CPHelper/CPHelper.cs new file mode 100644 index 0000000..7429a2f --- /dev/null +++ b/CPHelper/CPHelper.cs @@ -0,0 +1,1342 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Mapper; +using MobDB; +using ProxyCore; +using ProxyCore.Input; +using ProxyCore.Output; +using ProxyCore.Scripting; + +namespace CPHelper +{ + public class CPHelper : Plugin + { + public CPHelper() + : base("cphelper", "Campaign Helper") + { + Author = "Duckbat"; + Version = 5; + Description = "Matches mobs from database to rooms. Creates useful tags for campaign check."; + UpdateUrl = "www.duckbat.com/plugins/update.cphelper.txt"; + Website = "code.google.com/p/proxymud/"; + + Config = new CPHelperConfig(); + + RegisterCommand("autonoexp", @"^(\d+)", AutoNoexpCommand); + RegisterCommand("gowhere", "", GoWhereCommand, 3); + RegisterCommand("where", "(.+)", WhereCommand, 3); + RegisterCommand("gquest", "(.+)", GQCheckCommand, 2); + RegisterCommand("campaign", "(.+)", CampaignCheckCommand, 5); + RegisterCommand("cp", "(.+)", CampaignCheckCommand, 0); + RegisterCommand("awhere", "(.+)", AutoWhereCommand, 2); + RegisterCommand("ahunttrick", "(.+)", AutoHuntTrickCommand, 2); + RegisterCommand("hunt", "(.+)", HuntCommand, 2); + RegisterCommand("astop", "", AutoStopCommand, 2); + + RegisterTrigger("gq.quit", @"@wYou are no longer part of the current quest.", TriggerGQLevel2, TriggerFlags.NotRegex); + RegisterTrigger("hunt.target", @"@wYou seem unable to hunt that target for some reason.", TriggerHuntT, TriggerFlags.NotRegex); + RegisterTrigger("hunt.fail1", @"@wNo one in this area by that name.", TriggerHuntFail, TriggerFlags.NotRegex); + RegisterTrigger("hunt.fail2", @"^@wYou couldn't find a path to .+from here\.$", TriggerHuntNT); + RegisterTrigger("hunt.fail3", @"(.+?)is here!$", TriggerHuntFail); + RegisterTrigger("hunt.nottarget", @"^@wYou are confident that .+passed through here, heading \w+\.$", TriggerHuntNT); + RegisterTrigger("hunt.nottarget2", @"^@wYou have no idea what you're doing, but maybe .+left .+\?$", TriggerHuntNT); + RegisterTrigger("hunt.nottarget3", @"^@wYou are almost certain that .+is .+from here\.$", TriggerHuntNT); + RegisterTrigger("hunt.nottarget4", @"^@wThe trail of .+is confusing, but you're reasonable sure .+headed .+\.$", TriggerHuntNT); + RegisterTrigger("hunt.nottarget5", @"^@wThere are traces of .+having been here\. Perhaps they lead .+\?$", TriggerHuntNT); + RegisterTrigger("hunt.nottarget6", @"^@wYou are certain that .+is .+from here\.$", TriggerHuntNT); + RegisterTrigger("where.fail1", @"@wThere are too many doors and fences to see who is in this area.", TriggerWhereFail, TriggerFlags.NotRegex); + RegisterTrigger("where.fail2", @"^@wThere is no .+around here\.$", TriggerWhereFail); + RegisterTrigger("quest0", @"^\$gmcp\.comm\.quest\.action start$", TriggerQuest0); + RegisterTrigger("quest1", @"^\$gmcp\.comm\.quest\.room (.+)", TriggerQuest2); + RegisterTrigger("quest2", @"^\$gmcp\.comm\.quest\.area (.+)", TriggerQuest3); + RegisterTrigger("quest3", @"^\$gmcp\.comm\.quest\.targ (.+)", TriggerQuest1); + RegisterTrigger("enterroom", @"^\$gmcp\.room\.info\.num (-?\d+)$", TriggerRoomNum); + RegisterTrigger("check", @"^@wYou still have to kill (@c\d @w)?\* (.+?) (@w)?\((.+?)(@w)?\)$", TriggerCheck); + RegisterTrigger("char.level", @"^\$gmcp\.char\.status\.level (\d+)$", TriggerLevel); + RegisterTrigger("char.tnl", @"^\$gmcp\.char\.status\.tnl (\d+)$", TriggerTNL); + RegisterTrigger("request", @"^@.Commander Barcett tells you 'Good luck in your campaign!'$", TriggerRequest); + RegisterTrigger("cplevel", @"^@cLevel Taken\.\.\.\.\.\.\.\.: @g\[ \s+@w(\d+) @g\]$", TriggerCPLevel); + RegisterTrigger("gqlevel", @"^@RGlobal Quest@Y: @gGlobal quest # \d+ @whas been declared for levels @g(\d+) @wto @g(\d+)@w\.$", TriggerGQLevel); + RegisterTrigger("gqlevel2", @"^@RGlobal Quest@Y: @wThe global quest has been won by @Y\w+ @w- @Y\d+(st|th|rd|nd) @wwin\.$", TriggerGQLevel2); + RegisterTrigger("noexp1", @"@RYou will no longer receive experience. Happy questing!", NoexpTrigger1, TriggerFlags.NotRegex); + RegisterTrigger("noexp2", @"@wYou will now receive experience. Happy leveling!", NoexpTrigger2, TriggerFlags.NotRegex); + RegisterTrigger("cpcomplete", @"@GCONGRATULATIONS! @wYou have completed your campaign.", TriggerCPComplete, TriggerFlags.NotRegex); + RegisterTrigger("cpfail", "@wCampaign cleared.", TriggerCPComplete, TriggerFlags.NotRegex); + RegisterTrigger("scan1", "@w{scan}", TriggerScan1, TriggerFlags.NotRegex); + RegisterTrigger("scan0", "@w{/scan}", TriggerScan0, TriggerFlags.NotRegex); + RegisterTrigger("scan2", "^ @w- (.+)", TriggerScan2); + RegisterTrigger("scan.room", @"^@C(\d )?(North|East|West|South|Down|Up) from here you see:", TriggerScanRoom); + RegisterTrigger("scan.room2", @"^@CRight here you see:", TriggerScanRoom2); + RegisterTrigger("enemy", @"^$gmcp\.char\.status\.enemy(.*)", TriggerCurrentEnemy); + RegisterTrigger("cp.kill", @"@WCongratulations, that was one of your CAMPAIGN mobs!", TriggerOnKilled1, TriggerFlags.NotRegex); + RegisterTrigger("gq.kill", @"@RCongratulations, that was one of the GLOBAL QUEST mobs!", TriggerOnKilled0, TriggerFlags.NotRegex); + RegisterTrigger("room.area", @"^\$gmcp\.room\.info\.zone (.*)$", TriggerRoomInfoArea); + RegisterTrigger("where", @"^(.{28}) (.+)", TriggerWhere, TriggerFlags.NonAnsi); + } + + private uint CurrentRoomId = uint.MaxValue; + private bool AllowScan = false; + private int MyLvl = 0; + private int MyXP = 99999; + private int CampaignLvl = 0; + private int GQLvl = 0; + private long Spam = 0; + private bool IsNoexp = false; + private int AutoNoexp = 0; + private bool didEnter = false; + private readonly List[] Targets = new[] { new List(), new List() }; + private string CurEnemy = ""; + private string RoomInfoArea = ""; + private long WhereTimeout = 0; + private long HuntTimeout = 0; + private readonly List WhereRooms = new List(); + private uint FirstWhereRoom = uint.MaxValue; + private readonly List ScanRooms = new List(); + private uint CurScanRoom = uint.MaxValue; + private string QuestTarget = string.Empty; + private string QuestRoom = string.Empty; + private string QuestArea = string.Empty; + private bool QuestListen = false; + + private int AutoWhereNth = 1; + private string AutoWhereKey = string.Empty; + private int AutoHuntNth = 1; + private string AutoHuntKey = string.Empty; + + private bool TriggerHuntT(TriggerData t) + { + World.Instance.Execute("where " + AutoHuntNth + "." + AutoHuntKey, true); + AutoHuntKey = string.Empty; + AutoHuntNth = 1; + return false; + } + + private bool TriggerHuntNT(TriggerData t) + { + HuntTimeout = 0; + AutoHuntNth++; + return false; + } + + private bool TriggerWhereFail(TriggerData t) + { + AutoWhereKey = string.Empty; + AutoWhereNth = 1; + WhereTimeout = 0; + return false; + } + + private bool TriggerHuntFail(TriggerData t) + { + AutoHuntKey = string.Empty; + AutoHuntNth = 1; + HuntTimeout = 0; + return false; + } + + private bool AutoStopCommand(InputData i) + { + AutoWhereKey = string.Empty; + AutoHuntKey = string.Empty; + return true; + } + + private bool AutoWhereCommand(InputData i) + { + string arg; + if(!i.Arguments.Success || (arg = i.Arguments.Groups[1].Value.Trim()).Length == 0) + { + World.Instance.SendMessage("@wSyntax: awhere ", i.ClientMask); + return true; + } + + WhereTimeout = 0; + Match m = _argRegex.Match(arg); + if(m.Success) + { + AutoWhereKey = m.Groups[2].Value; + if(!int.TryParse(m.Groups[1].Value, out AutoWhereNth)) + AutoWhereNth = 1; + } + else + { + AutoWhereNth = 1; + AutoWhereKey = arg; + } + return true; + } + + private bool AutoHuntTrickCommand(InputData i) + { + string arg; + if(!i.Arguments.Success || (arg = i.Arguments.Groups[1].Value.Trim()).Length == 0) + { + World.Instance.SendMessage("@wSyntax: ahunttrick ", i.ClientMask); + return true; + } + + HuntTimeout = 0; + Match m = _argRegex.Match(arg); + if(m.Success) + { + AutoHuntKey = m.Groups[2].Value; + if(!int.TryParse(m.Groups[1].Value, out AutoHuntNth)) + AutoHuntNth = 1; + } + else + { + AutoHuntNth = 1; + AutoHuntKey = arg; + } + return true; + } + + private static Regex _argRegex = new Regex(@"^(\d+)\.(.+)", RegexOptions.Compiled); + + private bool TriggerQuest0(TriggerData t) + { + QuestListen = true; + return false; + } + + private bool TriggerQuest1(TriggerData t) + { + if(!QuestListen) + return false; + + QuestTarget = MobDB.MobDB.NormalizeName(t.Match.Groups[1].Value.Trim()); + return false; + } + + private bool TriggerQuest2(TriggerData t) + { + if(!QuestListen) + return false; + + QuestRoom = t.Match.Groups[1].Value.Trim(); + return false; + } + + private bool TriggerQuest3(TriggerData t) + { + if(!QuestListen) + return false; + + QuestListen = false; + QuestArea = t.Match.Groups[1].Value.Trim(); + + int minLevel = MyLvl - 8; + int maxLevel = MyLvl + 10; + if(MyLvl >= 200) + maxLevel = MyLvl + 20; + + uint bestMobRoom = uint.MaxValue; + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + List Areas = new List(); + List Rooms = new List(); + foreach(Area x in map.Areas) + { + if(x.Name == QuestArea) + Areas.Add(x.Keyword); + } + foreach(Room r in map.Rooms) + { + if(r.Area.Name != QuestArea) + continue; + + if(r.Name != QuestRoom) + continue; + + Rooms.Add(r.Entry); + } + foreach(Mob m in db.Mobs) + { + if(m.Level < minLevel || m.Level > maxLevel) + continue; + + if(!m.Name.Contains(QuestTarget)) + continue; + + if(!m.Areas.Contains("all")) + { + bool f = false; + foreach(string x in m.Areas) + { + if(Areas.Contains(x)) + { + f = true; + break; + } + } + + if(!f) + continue; + } + + bestMobRoom = m.GetBestRoom(); + if(bestMobRoom == uint.MaxValue) + continue; + + Room br = map.GetRoom(bestMobRoom); + if(br != null) + { + Pathfinder_Mob pf = new Pathfinder_Mob(m, Rooms.ToArray()); + pf.StartRooms = new[] { br }; + PathfindResult pr = map.Get(pf); + if(!pr.Success || pr.Target == null) + { + bestMobRoom = uint.MaxValue; + continue; + } + + bestMobRoom = pr.Target.Entry; + break; + } + } + + + if(Rooms.Count != 0) + { + WhereRooms.Clear(); + WhereRooms.AddRange(Rooms); + FirstWhereRoom = bestMobRoom; + World.Instance.SendMessage("@wAdded @C" + Rooms.Count + " @wroom" + (Rooms.Count != 1 ? "s" : "") + " to @G\"where\" @wlist. Use '@Wgowhere@w' to visit them."); + } + + return false; + } + + private bool TriggerScanRoom(TriggerData t) + { + CurScanRoom = uint.MaxValue; + int c = 1; + if(t.Match.Groups[1].Length != 0) + int.TryParse(t.Match.Groups[1].Value, out c); + char d = t.Match.Groups[2].Value.ToLower()[0]; + + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(map == null) + return false; + + Room r = map.GetRoom(CurrentRoomId); + if(r == null) + return false; + + for(int i = 0; i < c; i++) + { + Exit e = r.GetExit(d); + if(e == null) + return false; + + r = e.To; + } + + if(string.IsNullOrEmpty(r.Name)) + return false; + + CurScanRoom = r.Entry; + return false; + } + + private bool TriggerScanRoom2(TriggerData t) + { + CurScanRoom = CurrentRoomId; + return false; + } + + private bool GQCheckCommand(InputData i) + { + if(i.Arguments.Success && i.Arguments.Groups[1].Length <= 5 && + "check".StartsWith(i.Arguments.Groups[1].Value.ToLower())) + Targets[0].Clear(); + return false; + } + + private bool CampaignCheckCommand(InputData i) + { + if(i.Arguments.Success && i.Arguments.Groups[1].Length <= 5 && + "check".StartsWith(i.Arguments.Groups[1].Value.ToLower())) + Targets[1].Clear(); + return false; + } + + private bool TriggerRoomNum(TriggerData t) + { + uint u; + if(uint.TryParse(t.Match.Groups[1].Value, out u)) + { + CurrentRoomId = u; + if(FirstWhereRoom == u) + FirstWhereRoom = uint.MaxValue; + if(WhereRooms.Remove(u)) + World.Instance.SendMessage("@wEntered a @G\"where\" @wroom. Now have @C" + WhereRooms.Count + " @wroom" + (WhereRooms.Count != 1 ? "s" : "") + " remaining. Use '@Wgowhere@w' to go to the next one."); + } + else + CurrentRoomId = uint.MaxValue; + return false; + } + + private bool TriggerWhere(TriggerData t) + { + if(WhereTimeout < World.Instance.MSTime) + return false; + + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(map == null) + return false; + + string rName = t.Match.Groups[2].Value.Trim(); + + List r = new List(); + foreach(Room x in map.Rooms) + { + if(x.Area.Keyword == RoomInfoArea && x.Name == rName) + r.Add(x); + } + + string mobName = t.Match.Groups[1].Value.Trim(); + mobName = MobDB.MobDB.NormalizeName(mobName); + + if(!string.IsNullOrEmpty(AutoWhereKey)) + { + if(Targets[0].Count != 0) + { + foreach(CPEntry x in Targets[0]) + { + if(x.Name.StartsWith(mobName)) + { + AutoWhereKey = string.Empty; + break; + } + } + } + else + { + foreach(CPEntry x in Targets[1]) + { + if(x.Name.StartsWith(mobName)) + { + AutoWhereKey = string.Empty; + break; + } + } + + if(QuestTarget.StartsWith(mobName)) + AutoWhereKey = string.Empty; + } + } + + if(r.Count == 0) + return false; + + WhereRooms.Clear(); + FirstWhereRoom = uint.MaxValue; + WhereTimeout = 0; + foreach(Room x in r) + { + if(CurrentRoomId != x.Entry) + WhereRooms.Add(x.Entry); + } + + if(WhereRooms.Count == 0) + return false; + + World.Instance.SendMessage("@wAdded @C" + WhereRooms.Count + " @wroom" + (WhereRooms.Count != 1 ? "s" : "") + " to @G\"where\"@w. Type '@Wgowhere@w' to go there."); + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + return false; + + foreach(Mob m in db.Mobs) + { + if(!m.Areas.Contains("all") && !m.Areas.Contains(RoomInfoArea)) + continue; + + bool f = false; + foreach(string z in m.Name) + { + if(mobName.Length >= 26) + { + if(z.StartsWith(mobName)) + { + f = true; + break; + } + } + else if(z == mobName) + { + f = true; + break; + } + } + + if(!f) + continue; + + uint b = m.GetBestRoom(); + if(b == uint.MaxValue || b == CurrentRoomId) + continue; + + Room br = map.GetRoom(b); + if(br == null || br.Entry == CurrentRoomId) + continue; + + if(WhereRooms.Contains(b)) + FirstWhereRoom = b; + else + { + Pathfinder_Mob pf = new Pathfinder_Mob(m, WhereRooms.ToArray()); + pf.StartRooms = new[] { br }; + + PathfindResult pr = map.Get(pf); + if(pr.Success && pr.Target != null) + FirstWhereRoom = pr.Target.Entry; + } + + break; + } + + return false; + } + + private bool WhereCommand(InputData i) + { + if(i.Arguments.Success && i.Arguments.Groups[1].Value.Trim().Length > 0) + WhereTimeout = World.Instance.MSTime + 6000; + return false; + } + + private bool HuntCommand(InputData i) + { + if(i.Arguments.Success && i.Arguments.Groups[1].Value.Trim().Length > 0) + HuntTimeout = World.Instance.MSTime + 6000; + return false; + } + + private bool GoWhereCommand(InputData i) + { + if(WhereRooms.Count == 0) + World.Instance.SendMessage("@wThere are no where rooms available.", i.ClientMask); + else + { + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(map == null) + { + World.Instance.SendMessage("@wInternal error.", i.ClientMask); + return true; + } + + Pathfinder_Entry pf = new Pathfinder_Entry(FirstWhereRoom != uint.MaxValue ? new[] { FirstWhereRoom } : WhereRooms.ToArray()); + map.FillPathFinder(pf); + PathfindResult pr = map.Get(pf); + if(!pr.Success) + World.Instance.SendMessage("@wCouldn't find a path to any of the where rooms.", i.ClientMask); + else + map.Goto(pr); + } + return true; + } + + public override void OnLogin() + { + base.OnLogin(); + + RegisterCommand("goto", @"(.+)", GotoMobCommand, 2, CMDFlags.None, "mobdb"); + } + + private bool GotoMobCommand(InputData i) + { + if(string.IsNullOrEmpty(RoomInfoArea)) + { + World.Instance.SendMessage("@wDon't know yet which area we are in. Type look to find out.", i.ClientMask); + return true; + } + + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: mobdb goto ", i.ClientMask); + return true; + } + + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + { + World.Instance.SendMessage("@wInternal error.", i.ClientMask); + return true; + } + + List toGoRooms = new List(); + string name = i.Arguments.Groups[1].Value.ToLower(); + foreach(Mob m in db.Mobs) + { + if(!m.Areas.Contains("all") && !m.Areas.Contains(RoomInfoArea)) + continue; + + uint p = m.GetBestRoom(); + if(p == uint.MaxValue) + continue; + + foreach(string z in m.Name) + { + if(z.ToLower().Contains(name)) + { + if(!toGoRooms.Contains(p)) + toGoRooms.Add(p); + break; + } + } + } + + if(toGoRooms.Count == 0) + { + World.Instance.SendMessage("@wFound no mobs with that name in current area.", i.ClientMask); + return true; + } + + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(map == null) + { + World.Instance.SendMessage("@wInternal error.", i.ClientMask); + return true; + } + + Pathfinder_Entry pf = new Pathfinder_Entry(toGoRooms.ToArray()); + if(!map.FillPathFinder(pf)) + { + World.Instance.SendMessage("@wWe are in an unknown room.", i.ClientMask); + return true; + } + + PathfindResult pr = map.Get(pf); + if(!pr.Success) + { + World.Instance.SendMessage("@wCouldn't find a path to any of the mobs with that name.", i.ClientMask); + return true; + } + + map.Goto(pr); + return true; + } + + private bool TriggerRoomInfoArea(TriggerData t) + { + RoomInfoArea = t.Match.Groups[1].Value.Trim(); + return false; + } + + private bool TriggerOnKilled0(TriggerData t) + { + if(string.IsNullOrEmpty(CurEnemy)) + return false; + + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + return false; + + CurEnemy = MobDB.MobDB.NormalizeName(CurEnemy); + + for(int i = 0; i < Targets[0].Count; i++) + { + CPEntry x = Targets[0][i]; + + if(x.Name != CurEnemy) + continue; + + foreach(uint u in x.Mobs) + { + Mob m = db.GetMob(u); + if(m == null) + continue; + + if(!m.Areas.Contains("all") && !m.Areas.Contains(RoomInfoArea)) + continue; + + if(m.Name.Contains(CurEnemy)) + { + if(x.Count > 1) + { + x.Count--; + return false; + } + + Targets[0].RemoveAt(i); + UpdateColors(); + return false; + } + } + } + + return false; + } + + private bool TriggerOnKilled1(TriggerData t) + { + if(string.IsNullOrEmpty(CurEnemy)) + return false; + + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + return false; + + CurEnemy = MobDB.MobDB.NormalizeName(CurEnemy); + + for(int i = 0; i < Targets[1].Count; i++) + { + CPEntry x = Targets[1][i]; + + if(x.Name != CurEnemy) + continue; + + foreach(uint u in x.Mobs) + { + Mob m = db.GetMob(u); + if(m == null) + continue; + + if(!m.Areas.Contains("all") && !m.Areas.Contains(RoomInfoArea)) + continue; + + if(m.Name.Contains(CurEnemy)) + { + if(x.Count > 1) + { + x.Count--; + return false; + } + + Targets[1].RemoveAt(i); + UpdateColors(); + return false; + } + } + } + + return false; + } + + private bool TriggerCurrentEnemy(TriggerData t) + { + CurEnemy = Colors.RemoveColors(t.Match.Groups[1].Value, false).Trim(); + return false; + } + + private bool TriggerScan0(TriggerData t) + { + AllowScan = false; + if(Config.GetInt32("Tags.Scan.Gag", 1) != 0) + t.Msg.AuthMask = 0; + + StringBuilder strScan = new StringBuilder(); + foreach(uint u in ScanRooms) + { + if(strScan.Length > 0) + strScan.Append(" "); + strScan.Append(u); + } + World.Instance.SendMessage("@w{scan_rooms}" + strScan); + return false; + } + + private bool TriggerScan1(TriggerData t) + { + ScanRooms.Clear(); + AllowScan = true; + if(Config.GetInt32("Tags.Scan.Gag", 1) != 0) + t.Msg.AuthMask = 0; + return false; + } + + private bool TriggerScan2(TriggerData td) + { + if(!AllowScan) + return false; + + if(Targets[0].Count == 0 && Targets[1].Count == 0) + return false; + + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + return false; + + string t = Colors.RemoveDuplicateColors("@w" + td.Match.Groups[1].Value); + if(t.StartsWith("@R(Wounded)")) + t = Colors.RemoveDuplicateColors("@R" + t.Substring("@R(Wounded)".Length)).Trim(); + + if(t.StartsWith("@w(Invis)")) + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(Invis)".Length)).Trim(); + else if(t.StartsWith("@w(I)")) + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(I)".Length)).Trim(); + + if(t.StartsWith("@w(Hidden)")) + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(Hidden)".Length)).Trim(); + else if(t.StartsWith("@w(H)")) + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(H)".Length)).Trim(); + + if(t.StartsWith("@W(Translucent)")) + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(Translucent)".Length)).Trim(); + else if(t.StartsWith("@W(T)")) + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(T)".Length)).Trim(); + + if(t.StartsWith("@C(Charmed) ")) + return false; + else if(t.StartsWith("@C(C)")) + return false; + + if(t.StartsWith("@M(Animated) ")) + return false; + else if(t.StartsWith("@M(A)")) + return false; + + if(t.StartsWith("@B(Diseased)")) + t = Colors.RemoveDuplicateColors("@B" + t.Substring("@B(Diseased)".Length)).Trim(); + else if(t.StartsWith("@B(D)")) + t = Colors.RemoveDuplicateColors("@B" + t.Substring("@B(D)".Length)).Trim(); + + if(t.StartsWith("@D(Marked)")) + t = Colors.RemoveDuplicateColors("@D" + t.Substring("@D(Marked)".Length)).Trim(); + else if(t.StartsWith("@D(X)")) + t = Colors.RemoveDuplicateColors("@D" + t.Substring("@D(X)".Length)).Trim(); + + if(t.StartsWith("@r(Red Aura)")) + t = Colors.RemoveDuplicateColors("@r" + t.Substring("@r(Red Aura)".Length)).Trim(); + else if(t.StartsWith("@r(R)")) + t = Colors.RemoveDuplicateColors("@r" + t.Substring("@r(R)".Length)).Trim(); + else if(t.StartsWith("@y(Golden Aura)")) + t = Colors.RemoveDuplicateColors("@y" + t.Substring("@y(Golden Aura)".Length)).Trim(); + else if(t.StartsWith("@y(G)")) + t = Colors.RemoveDuplicateColors("@y" + t.Substring("@y(G)".Length)).Trim(); + + if(t.StartsWith("@W(White Aura)")) + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(White Aura)".Length)).Trim(); + else if(t.StartsWith("@W(W)")) + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(W)".Length)).Trim(); + + if(t.StartsWith("@R(Angry)")) + t = Colors.RemoveDuplicateColors("@R" + t.Substring("@R(Angry)".Length)).Trim(); + + if(t.StartsWith("@w(Linkdead)")) + return false; + + if(t.StartsWith("@R(RAIDER)")) + return false; + + if(t.StartsWith("@R(TRAITOR)")) + return false; + + if(t.StartsWith("@G(DEFENDER)")) + return false; + + if(t.StartsWith("@R(WANTED)")) + return false; + + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(map == null) + return false; + + string n = Colors.RemoveColors(t, false).ToLower().Trim(); + if(t.StartsWith("@") && !t.StartsWith("@@") && t.Length >= 2) + t = t.Substring(2); + if(Targets[0].Count != 0) + { + foreach(CPEntry x in Targets[0]) + { + if(x.Name.ToLower() != n) + continue; + + int ind = td.Msg.Msg.LastIndexOf(t); + if(ind != -1) + { + td.Msg.Msg = td.Msg.Msg.Remove(ind); + td.Msg.Msg = td.Msg.Msg.Insert(ind, Config.GetString("Color.Scan", "@R") + x.Name); + } + + if(CurScanRoom != uint.MaxValue && !ScanRooms.Contains(CurScanRoom)) + ScanRooms.Add(CurScanRoom); + + return false; + } + } + else + { + foreach(CPEntry x in Targets[1]) + { + if(x.Name.ToLower() == n) + { + int ind = td.Msg.Msg.LastIndexOf(t); + if(ind != -1) + { + td.Msg.Msg = td.Msg.Msg.Remove(ind); + td.Msg.Msg = td.Msg.Msg.Insert(ind, Config.GetString("Color.Scan", "@R") + x.Name); + } + + if(CurScanRoom != uint.MaxValue && !ScanRooms.Contains(CurScanRoom)) + ScanRooms.Add(CurScanRoom); + + return false; + } + } + } + + return false; + } + + private bool TriggerCPComplete(TriggerData t) + { + Targets[1].Clear(); + UpdateColors(); + return false; + } + + private bool NoexpTrigger1(TriggerData t) + { + IsNoexp = true; + didEnter = false; + return false; + } + + private bool NoexpTrigger2(TriggerData t) + { + IsNoexp = false; + didEnter = false; + return false; + } + + public override void Update(long msTime) + { + base.Update(msTime); + + if(!string.IsNullOrEmpty(AutoWhereKey)) + { + if(WhereTimeout > msTime) + return; + if(WhereTimeout != 0) + AutoWhereKey = string.Empty; + else + { + World.Instance.Execute("where " + AutoWhereNth + "." + AutoWhereKey, true); + AutoWhereNth++; + return; + } + } + + if(!string.IsNullOrEmpty(AutoHuntKey)) + { + if(HuntTimeout > msTime) + return; + if(HuntTimeout != 0) + AutoHuntKey = string.Empty; + else + { + World.Instance.Execute("hunt " + AutoHuntNth + "." + AutoHuntKey, true); + return; + } + } + + if(didEnter) + return; + + if(AutoNoexp == 0) + return; + + bool shouldNoExp = MyLvl > CampaignLvl && MyXP <= AutoNoexp; + if(shouldNoExp) + { + if(IsNoexp) + return; + } + else + { + if(!IsNoexp) + return; + } + + World.Instance.Execute("noexp", true); + didEnter = true; + } + + private bool AutoNoexpCommand(InputData t) + { + int n; + if(!t.Arguments.Success || !int.TryParse(t.Arguments.Groups[1].Value, out n)) + { + World.Instance.SendMessage("@wSyntax: autonoexp ", t.ClientMask); + return true; + } + + if(n <= 0) + { + AutoNoexp = 0; + World.Instance.SendMessage("@wYou will no longer turn noexp on automatically.", t.ClientMask); + return true; + } + + AutoNoexp = n; + World.Instance.SendMessage("@wYou will now turn noexp on when you can ask a campaign and XP goes to @C" + n + " @wor below.", t.ClientMask); + World.Instance.SendMessage("@wUse '@Wautonoexp 0@w' to turn this off.", t.ClientMask); + return true; + } + + private bool TriggerGQLevel(TriggerData t) + { + Targets[0].Clear(); + int lvl; + if(int.TryParse(t.Match.Groups[1].Value, out lvl)) + GQLvl = lvl; + UpdateColors(); + return false; + } + + private bool TriggerGQLevel2(TriggerData t) + { + Targets[0].Clear(); + GQLvl = 0; + UpdateColors(); + return false; + } + + private bool TriggerCPLevel(TriggerData t) + { + int lvl; + if(int.TryParse(t.Match.Groups[1].Value, out lvl)) + CampaignLvl = lvl; + return false; + } + + private bool TriggerRequest(TriggerData t) + { + CampaignLvl = MyLvl; + return false; + } + + private bool TriggerLevel(TriggerData t) + { + int lvl; + if(int.TryParse(t.Match.Groups[1].Value, out lvl)) + MyLvl = lvl; + return false; + } + + private bool TriggerTNL(TriggerData t) + { + int lvl; + if(int.TryParse(t.Match.Groups[1].Value, out lvl)) + MyXP = lvl; + return false; + } + + private bool TriggerCheck(TriggerData t) + { + string mobName = MobDB.MobDB.NormalizeName(Colors.RemoveColors(t.Match.Groups[2].Value, false).Trim()); + string placeName = Colors.RemoveColors(t.Match.Groups[4].Value, false).Trim(); + if(placeName.EndsWith(" - Dead")) + placeName = placeName.Substring(0, placeName.LastIndexOf(" - Dead")); + + if(Config.GetInt32("Tags.On", 1) != 0) + { + World.Instance.SendMessage("@w{cp_mob}" + mobName); + World.Instance.SendMessage("@w{cp_place}" + placeName); + } + + if(CampaignLvl == 0 && t.Match.Groups[1].Length == 0) + { + if(Spam < World.Instance.MSTime) + { + Spam = World.Instance.MSTime + 300; + World.Instance.SendMessage("@WPlease see '@Rcampaign info@W' first! I need the level to check for mobs."); + } + return false; + } + + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + if(db == null || map == null) + return false; + + int minLevel = CampaignLvl - 5; + int maxLevel = CampaignLvl + 12; + if(CampaignLvl >= 200) + maxLevel = CampaignLvl + 20; + + if(t.Match.Groups[1].Length != 0) + { + minLevel = MyLvl - 20; + maxLevel = MyLvl + 10; + if(GQLvl != 0) + { + minLevel = GQLvl - 6; + maxLevel = GQLvl + 15; + if(GQLvl >= 186) + maxLevel = GQLvl + 21; + } + } + + List Rooms = new List(); + List Areas = new List(); + foreach(Area z in map.Areas) + { + if(z.Name == placeName) + Areas.Add(z.Keyword); + } + + CPEntry e = new CPEntry(); + e.Name = mobName; + e.PlaceName = placeName; + + if(Areas.Count != 0) + { + // Area CP / GQ? + foreach(string a in Areas) + { + foreach(Mob x in db.Mobs) + { + if(x.Level < minLevel || x.Level > maxLevel) + continue; + if(!x.Areas.Contains("all") && !x.Areas.Contains(a)) + continue; + + if(!x.Name.Contains(mobName)) + continue; + + uint roomId = x.GetBestRoom(); + if(roomId == uint.MaxValue) + continue; + + Rooms.Add(roomId); + if(!e.Mobs.Contains(x.Entry)) + e.Mobs.Add(x.Entry); + } + } + } + else + { + List r = new List(); + List p = new List(); + foreach(Room x in map.Rooms) + { + if(x.Name == placeName) + { + r.Add(x.Entry); + if(!p.Contains(x.Area.Keyword)) + p.Add(x.Area.Keyword); + } + } + + foreach(Mob x in db.Mobs) + { + if(x.Level < minLevel || x.Level > maxLevel) + continue; + if(!x.Areas.Contains("all")) + { + bool f = false; + foreach(string z in x.Areas) + { + if(p.Contains(z)) + { + f = true; + break; + } + } + + if(!f) + continue; + } + + if(!x.Name.Contains(mobName)) + continue; + + if(!e.Mobs.Contains(x.Entry)) + e.Mobs.Add(x.Entry); + + uint u = x.GetBestRoom(); + if(u == uint.MaxValue) + continue; + + Room br = map.GetRoom(u); + if(br == null) + continue; + + Pathfinder_Mob pf = new Pathfinder_Mob(x, r.ToArray()); + pf.StartRooms = new[] { br }; + PathfindResult pr = map.Get(pf); + if(pr.Success && pr.Target != null) + u = pr.Target.Entry; + + if(!Rooms.Contains(u)) + Rooms.Add(u); + } + } + + if(t.Match.Groups[1].Length == 0) + e.Count = 1; + else + { + int n; + if(int.TryParse(Colors.RemoveColors(t.Match.Groups[1].Value, false).Trim(), out n)) + { + e.Count = n; + e.IsGQ = true; + } + else + { + e.Count = 1; + e.IsGQ = true; + } + } + + if(e.IsGQ) + Targets[0].Add(e); + else + Targets[1].Add(e); + + StringBuilder strRooms = new StringBuilder(); + foreach(uint u in Rooms) + { + if(strRooms.Length > 0) + strRooms.Append(" "); + strRooms.Append(u); + } + World.Instance.SendMessage("@w{cp_room}" + strRooms); + + UpdateColors(); + return false; + } + + private void UpdateColors() + { + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(db == null) + return; + + db.ClearMobColors(); + if(string.IsNullOrEmpty(Config.GetString("Color.Room", "@C"))) + return; + + if(Targets[0].Count != 0) + { + foreach(CPEntry x in Targets[0]) + { + foreach(uint u in x.Mobs) + db.SetMobColor(u, Config.GetString("Color.Room", "@C")); + } + } + else + { + foreach(CPEntry x in Targets[1]) + { + foreach(uint u in x.Mobs) + db.SetMobColor(u, Config.GetString("Color.Room", "@C")); + } + } + } + + private CPEntry IsSame(List entries, string mobName, string placeName) + { + Mapper.Mapper map = PluginMgr.GetPlugin("mapper") as Mapper.Mapper; + MobDB.MobDB db = PluginMgr.GetPlugin("mobdb") as MobDB.MobDB; + if(map == null || db == null) + return null; + + List areas = new List(); + foreach(Area x in map.Areas) + { + if(x.Name == placeName) + areas.Add(x.Keyword); + } + if(areas.Count == 0) + { + foreach(Room x in map.Rooms) + { + if(x.Name == placeName && !areas.Contains(x.Area.Keyword)) + areas.Add(x.Area.Keyword); + } + } + + foreach(CPEntry x in entries) + { + if(x.Name != mobName) + continue; + + foreach(uint u in x.Mobs) + { + Mob m = db.GetMob(u); + if(m == null) + continue; + + if(!m.Areas.Contains("all")) + { + bool f = false; + foreach(string y in m.Areas) + { + if(areas.Contains(y)) + { + f = true; + break; + } + } + + if(!f) + continue; + } + + return x; + } + } + + return null; + } + } + + public class Pathfinder_Mob : Pathfinder_Entry + { + public Pathfinder_Mob(Mob m, params uint[] roomId) + : base(roomId) + { + mob = m; + CanUsePortals = false; + CanUseRecalls = false; + CharacterLevel = m.Level; + CharacterTier = 0; + IsGlobalQuest = false; + IsSingleClassTier0 = false; + } + + private Mob mob; + private static string[] Normals = new[] { "n", "e", "s", "w", "u", "d" }; + + public override bool CanUseExit(Exit e) + { + if(!Normals.Contains(e.Command)) + return false; + + if(!mob.Areas.Contains("all") && !mob.Areas.Contains(e.To.Area.Keyword)) + return false; + + return base.CanUseExit(e); + } + } + + public class CPHelperConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Tags.On", 1, "Display tags on campaign / gq check messages for easier capture. If you turn tags off then room IDs will still be displayed if we find a mob in database."); + CreateSetting("Tags.Scan.Gag", 1, "Gag {scan} tags."); + CreateSetting("Color.Room", "@C", "Color campaign / gq mobs differently (the $mob.color value) when we have a CP / GQ mob target. Enter empty value to skip this. Mob has to be in database for this to work."); + CreateSetting("Color.Scan", "@R", "Color campaign / gq mobs differently when scanning. Enter empty value to skip this. You need to have \"tags scan on\" for this to work. Mob has to be in database for this to work."); + } + } + + internal class CPEntry + { + internal bool IsGQ = false; + internal string Name; + internal int Count; + internal string PlaceName; + internal List Mobs = new List(); + } +} diff --git a/CPHelper/CPHelper.csproj b/CPHelper/CPHelper.csproj new file mode 100644 index 0000000..a5c3c6b --- /dev/null +++ b/CPHelper/CPHelper.csproj @@ -0,0 +1,98 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {11D11063-A123-4F02-9C20-FB76C49A4B52} + Library + Properties + CPHelper + CPHelper + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + TRACE + prompt + 4 + + + + False + ..\Mapper\bin\Release\Mapper.dll + + + False + ..\MobDB\bin\Release\MobDB.dll + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/CPHelper/Properties/.svn/all-wcprops b/CPHelper/Properties/.svn/all-wcprops new file mode 100644 index 0000000..48556f4 --- /dev/null +++ b/CPHelper/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/30/trunk/CPHelper/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/!svn/ver/30/trunk/CPHelper/Properties/AssemblyInfo.cs +END diff --git a/CPHelper/Properties/.svn/desktop.ini b/CPHelper/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/Properties/.svn/dir-prop-base b/CPHelper/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/CPHelper/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/CPHelper/Properties/.svn/entries b/CPHelper/Properties/.svn/entries new file mode 100644 index 0000000..e7a83ae --- /dev/null +++ b/CPHelper/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/CPHelper/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-23T07:49:14.623991Z +30 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:42.968136Z +d030e90d841aa34a6cbe51f326db4af8 +2012-01-23T07:49:14.623991Z +30 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1428 + diff --git a/CPHelper/Properties/.svn/prop-base/desktop.ini b/CPHelper/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/Properties/.svn/props/desktop.ini b/CPHelper/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/CPHelper/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..69bf221 --- /dev/null +++ b/CPHelper/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CPHelper")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CPHelper")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("1010ed0e-d884-4c43-96d5-5549f787d8c4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CPHelper/Properties/.svn/text-base/desktop.ini b/CPHelper/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/Properties/.svn/tmp/desktop.ini b/CPHelper/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/Properties/.svn/tmp/prop-base/desktop.ini b/CPHelper/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/Properties/.svn/tmp/props/desktop.ini b/CPHelper/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/Properties/.svn/tmp/text-base/desktop.ini b/CPHelper/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/Properties/AssemblyInfo.cs b/CPHelper/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..69bf221 --- /dev/null +++ b/CPHelper/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CPHelper")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CPHelper")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("1010ed0e-d884-4c43-96d5-5549f787d8c4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CPHelper/Properties/desktop.ini b/CPHelper/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CPHelper/desktop.ini b/CPHelper/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CPHelper/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/.svn/all-wcprops b/CommandEcho/.svn/all-wcprops new file mode 100644 index 0000000..8ac1029 --- /dev/null +++ b/CommandEcho/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 33 +/svn/!svn/ver/7/trunk/CommandEcho +END +CommandEcho.cs +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/7/trunk/CommandEcho/CommandEcho.cs +END +CommandEcho.csproj +K 25 +svn:wc:ra_dav:version-url +V 52 +/svn/!svn/ver/7/trunk/CommandEcho/CommandEcho.csproj +END diff --git a/CommandEcho/.svn/desktop.ini b/CommandEcho/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/.svn/dir-prop-base b/CommandEcho/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/CommandEcho/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/CommandEcho/.svn/entries b/CommandEcho/.svn/entries new file mode 100644 index 0000000..cde5a6e --- /dev/null +++ b/CommandEcho/.svn/entries @@ -0,0 +1,99 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/CommandEcho +http://proxymud.googlecode.com/svn + + + +2012-01-18T13:01:52.613929Z +7 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +CommandEcho.cs +file + + + + +2016-03-25T22:18:43.232138Z +c4958e94321a3cbd77f4759bb9a6e2d6 +2012-01-18T13:01:52.613929Z +7 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2112 + +Properties +dir + +CommandEcho.csproj +file + + + + +2016-03-25T22:18:43.232138Z +1c5a5dcde35304dcd226132e20a22816 +2012-01-18T13:01:52.613929Z +7 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2727 + diff --git a/CommandEcho/.svn/prop-base/desktop.ini b/CommandEcho/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/.svn/props/desktop.ini b/CommandEcho/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/.svn/text-base/CommandEcho.cs.svn-base b/CommandEcho/.svn/text-base/CommandEcho.cs.svn-base new file mode 100644 index 0000000..5c015b8 --- /dev/null +++ b/CommandEcho/.svn/text-base/CommandEcho.cs.svn-base @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore; +using ProxyCore.Scripting; + +namespace CommandEcho +{ + public class CommandEcho : Plugin + { + public CommandEcho() + : base("cmdecho", "Command echo") + { + Author = "Duckbat"; + Version = 1; + Description = "Echos commands from certain security mask back to other clients. This way you can use your regular MUD client to execute another user's commands. For example if the user is on the phone or whereever."; + Website = "code.google.com/p/proxymud/"; + UpdateUrl = "www.duckbat.com/plugins/update.cmdecho.txt"; + + Config = new CMDEchoConfig(); + } + + public override void OnEnteredCommandAfter(ref string Msg, uint ClientId, int AuthLevel) + { + base.OnEnteredCommandAfter(ref Msg, ClientId, AuthLevel); + + ulong echoFrom = Config.GetUInt64("Echo.From.AuthMask", 1); + if((echoFrom & ((ulong)1 << (AuthLevel - 1))) != 0) + { + World.Instance.SendMessage("@w{CommandEcho,Level=" + AuthLevel + ",Id=" + ClientId + "}" + Msg, Config.GetUInt64("Echo.To.AuthMask", ulong.MaxValue - 1)); + Msg = null; + } + } + } + + public class CMDEchoConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Echo.From.AuthMask", 1, "Whose commands do we echo? This is a security mask. Default is 1 which means users with auth level 1 will send commands to other users."); + CreateSetting("Echo.To.AuthMask", (ulong.MaxValue - 1), "Who do we echo the commands to. Default is " + (ulong.MaxValue - 1) + " which means every security level except 1.\n\nCommands will be echoed in this format:\n@w{CommandEcho,Level=1,Id=1}say yay\nLevel means Auth level of client who entered command, Id means client id who entered command. If Id is 0 then command was entered from a plugin."); + } + } +} diff --git a/CommandEcho/.svn/text-base/CommandEcho.csproj.svn-base b/CommandEcho/.svn/text-base/CommandEcho.csproj.svn-base new file mode 100644 index 0000000..494b83e --- /dev/null +++ b/CommandEcho/.svn/text-base/CommandEcho.csproj.svn-base @@ -0,0 +1,63 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A51883B5-8A7B-414F-92A9-A171A9846411} + Library + Properties + CommandEcho + CommandEcho + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/CommandEcho/.svn/text-base/desktop.ini b/CommandEcho/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/.svn/tmp/desktop.ini b/CommandEcho/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/.svn/tmp/prop-base/desktop.ini b/CommandEcho/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/.svn/tmp/props/desktop.ini b/CommandEcho/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/.svn/tmp/text-base/desktop.ini b/CommandEcho/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/CommandEcho.cs b/CommandEcho/CommandEcho.cs new file mode 100644 index 0000000..5c015b8 --- /dev/null +++ b/CommandEcho/CommandEcho.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore; +using ProxyCore.Scripting; + +namespace CommandEcho +{ + public class CommandEcho : Plugin + { + public CommandEcho() + : base("cmdecho", "Command echo") + { + Author = "Duckbat"; + Version = 1; + Description = "Echos commands from certain security mask back to other clients. This way you can use your regular MUD client to execute another user's commands. For example if the user is on the phone or whereever."; + Website = "code.google.com/p/proxymud/"; + UpdateUrl = "www.duckbat.com/plugins/update.cmdecho.txt"; + + Config = new CMDEchoConfig(); + } + + public override void OnEnteredCommandAfter(ref string Msg, uint ClientId, int AuthLevel) + { + base.OnEnteredCommandAfter(ref Msg, ClientId, AuthLevel); + + ulong echoFrom = Config.GetUInt64("Echo.From.AuthMask", 1); + if((echoFrom & ((ulong)1 << (AuthLevel - 1))) != 0) + { + World.Instance.SendMessage("@w{CommandEcho,Level=" + AuthLevel + ",Id=" + ClientId + "}" + Msg, Config.GetUInt64("Echo.To.AuthMask", ulong.MaxValue - 1)); + Msg = null; + } + } + } + + public class CMDEchoConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Echo.From.AuthMask", 1, "Whose commands do we echo? This is a security mask. Default is 1 which means users with auth level 1 will send commands to other users."); + CreateSetting("Echo.To.AuthMask", (ulong.MaxValue - 1), "Who do we echo the commands to. Default is " + (ulong.MaxValue - 1) + " which means every security level except 1.\n\nCommands will be echoed in this format:\n@w{CommandEcho,Level=1,Id=1}say yay\nLevel means Auth level of client who entered command, Id means client id who entered command. If Id is 0 then command was entered from a plugin."); + } + } +} diff --git a/CommandEcho/CommandEcho.csproj b/CommandEcho/CommandEcho.csproj new file mode 100644 index 0000000..20b01f3 --- /dev/null +++ b/CommandEcho/CommandEcho.csproj @@ -0,0 +1,90 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A51883B5-8A7B-414F-92A9-A171A9846411} + Library + Properties + CommandEcho + CommandEcho + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/CommandEcho/Properties/.svn/all-wcprops b/CommandEcho/Properties/.svn/all-wcprops new file mode 100644 index 0000000..aa6850d --- /dev/null +++ b/CommandEcho/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/!svn/ver/7/trunk/CommandEcho/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 60 +/svn/!svn/ver/7/trunk/CommandEcho/Properties/AssemblyInfo.cs +END diff --git a/CommandEcho/Properties/.svn/desktop.ini b/CommandEcho/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/Properties/.svn/dir-prop-base b/CommandEcho/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/CommandEcho/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/CommandEcho/Properties/.svn/entries b/CommandEcho/Properties/.svn/entries new file mode 100644 index 0000000..8652594 --- /dev/null +++ b/CommandEcho/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/CommandEcho/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-18T13:01:52.613929Z +7 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:43.232138Z +35d863597b8948ecb9b10d9230e62387 +2012-01-18T13:01:52.613929Z +7 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1434 + diff --git a/CommandEcho/Properties/.svn/prop-base/desktop.ini b/CommandEcho/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/Properties/.svn/props/desktop.ini b/CommandEcho/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/CommandEcho/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..bafedd8 --- /dev/null +++ b/CommandEcho/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CommandEcho")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CommandEcho")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("fcd404fe-2f3b-4407-8fa1-f2063c2c7dc6")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CommandEcho/Properties/.svn/text-base/desktop.ini b/CommandEcho/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/Properties/.svn/tmp/desktop.ini b/CommandEcho/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/Properties/.svn/tmp/prop-base/desktop.ini b/CommandEcho/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/Properties/.svn/tmp/props/desktop.ini b/CommandEcho/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/Properties/.svn/tmp/text-base/desktop.ini b/CommandEcho/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/Properties/AssemblyInfo.cs b/CommandEcho/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..bafedd8 --- /dev/null +++ b/CommandEcho/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CommandEcho")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CommandEcho")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("fcd404fe-2f3b-4407-8fa1-f2063c2c7dc6")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CommandEcho/Properties/desktop.ini b/CommandEcho/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/CommandEcho/desktop.ini b/CommandEcho/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/CommandEcho/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/.svn/all-wcprops b/GMCPEcho/.svn/all-wcprops new file mode 100644 index 0000000..f1144b0 --- /dev/null +++ b/GMCPEcho/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 30 +/svn/!svn/ver/2/trunk/GMCPEcho +END +GMCPEcho.csproj +K 25 +svn:wc:ra_dav:version-url +V 46 +/svn/!svn/ver/2/trunk/GMCPEcho/GMCPEcho.csproj +END +GMCPEcho.cs +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/2/trunk/GMCPEcho/GMCPEcho.cs +END diff --git a/GMCPEcho/.svn/desktop.ini b/GMCPEcho/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/.svn/dir-prop-base b/GMCPEcho/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/GMCPEcho/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/GMCPEcho/.svn/entries b/GMCPEcho/.svn/entries new file mode 100644 index 0000000..ab05e7a --- /dev/null +++ b/GMCPEcho/.svn/entries @@ -0,0 +1,99 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/GMCPEcho +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +GMCPEcho.csproj +file + + + + +2016-03-25T22:18:43.064138Z +76fb0ddd83ae3b24fd40628d322b5e47 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2718 + +GMCPEcho.cs +file + + + + +2016-03-25T22:18:43.064138Z +9aaa0c4c8ac82fc1c1898d1cefd5cde5 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2717 + +Properties +dir + diff --git a/GMCPEcho/.svn/prop-base/desktop.ini b/GMCPEcho/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/.svn/props/desktop.ini b/GMCPEcho/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/.svn/text-base/GMCPEcho.cs.svn-base b/GMCPEcho/.svn/text-base/GMCPEcho.cs.svn-base new file mode 100644 index 0000000..cd254a7 --- /dev/null +++ b/GMCPEcho/.svn/text-base/GMCPEcho.cs.svn-base @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore.Scripting; +using ProxyCore; +using ProxyCore.Output; + +namespace GMCPEcho +{ + public class GMCPEcho : Plugin + { + public GMCPEcho() + : base("gmcpecho", "GMCP Echo") + { + Author = "Duckbat"; + Version = 1; + Description = "Echos all GMCP specified by user back. This way you can see GMCP in clients that normally don't support it."; + UpdateUrl = "www.duckbat.com/plugins/update.gmcpecho.txt"; + Website = "www.duckbat.com/plugins/index.php?t=gmcpecho"; + + Config = new GMCPEchoConfig(); + + RegisterTrigger("gmcp", @"^\$gmcp\.", GMCPTrigger); + } + + private string[] AllowedModules; + + private bool GMCPTrigger(TriggerData t) + { + if(AllowedModules == null) + AllowedModules = Config.GetString("GMCP.Modules", "gmcp.*").Split(new[] { ",", " " }, StringSplitOptions.RemoveEmptyEntries); + + string Module = t.Msg.Msg.Substring(1); + if(Module.Contains(' ')) + Module = Module.Substring(0, Module.IndexOf(' ')); + if(HasModule(Module)) + World.Instance.SendMessage(t.Msg.Msg, Config.GetUInt64("GMCP.AuthMask", ulong.MaxValue)); + return false; + } + + private bool HasModule(string Msg) + { + foreach(string x in AllowedModules) + { + if(x.EndsWith("*")) + { + if(Msg.StartsWith(x.Substring(0, x.Length - 1))) + return true; + continue; + } + + if(x == Msg) + return true; + } + + return false; + } + } + + public class GMCPEchoConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("GMCP.Modules", "gmcp.*", "Which GMCP modules would you like to echo to client. Separate with ',' sign and use '*' for wildcard but the wildcard can only be at the end not in the middle or start. For example \"gmcp.char.*, gmcp.room.*\". Use \"gmcp.*\" to echo everything."); + CreateSetting("GMCP.AuthMask", ulong.MaxValue, "Which client security levels see GMCP echo. This is a 64 bit mask. For example if you want security level 1 and 3 to see GMCP you would enter value 5 (1 + 4). These values are 2 ^ (level - 1), for example security level 3 mask: 2 ^ (3 - 1) = 4. Then you just add these up. Default (all levels): " + ulong.MaxValue); + } + } +} diff --git a/GMCPEcho/.svn/text-base/GMCPEcho.csproj.svn-base b/GMCPEcho/.svn/text-base/GMCPEcho.csproj.svn-base new file mode 100644 index 0000000..d527a43 --- /dev/null +++ b/GMCPEcho/.svn/text-base/GMCPEcho.csproj.svn-base @@ -0,0 +1,63 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {86B2AA8D-5B03-4128-9C77-9E94F71CFFEE} + Library + Properties + GMCPEcho + GMCPEcho + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/GMCPEcho/.svn/text-base/desktop.ini b/GMCPEcho/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/.svn/tmp/desktop.ini b/GMCPEcho/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/.svn/tmp/prop-base/desktop.ini b/GMCPEcho/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/.svn/tmp/props/desktop.ini b/GMCPEcho/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/.svn/tmp/text-base/desktop.ini b/GMCPEcho/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/GMCPEcho.cs b/GMCPEcho/GMCPEcho.cs new file mode 100644 index 0000000..cd254a7 --- /dev/null +++ b/GMCPEcho/GMCPEcho.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore.Scripting; +using ProxyCore; +using ProxyCore.Output; + +namespace GMCPEcho +{ + public class GMCPEcho : Plugin + { + public GMCPEcho() + : base("gmcpecho", "GMCP Echo") + { + Author = "Duckbat"; + Version = 1; + Description = "Echos all GMCP specified by user back. This way you can see GMCP in clients that normally don't support it."; + UpdateUrl = "www.duckbat.com/plugins/update.gmcpecho.txt"; + Website = "www.duckbat.com/plugins/index.php?t=gmcpecho"; + + Config = new GMCPEchoConfig(); + + RegisterTrigger("gmcp", @"^\$gmcp\.", GMCPTrigger); + } + + private string[] AllowedModules; + + private bool GMCPTrigger(TriggerData t) + { + if(AllowedModules == null) + AllowedModules = Config.GetString("GMCP.Modules", "gmcp.*").Split(new[] { ",", " " }, StringSplitOptions.RemoveEmptyEntries); + + string Module = t.Msg.Msg.Substring(1); + if(Module.Contains(' ')) + Module = Module.Substring(0, Module.IndexOf(' ')); + if(HasModule(Module)) + World.Instance.SendMessage(t.Msg.Msg, Config.GetUInt64("GMCP.AuthMask", ulong.MaxValue)); + return false; + } + + private bool HasModule(string Msg) + { + foreach(string x in AllowedModules) + { + if(x.EndsWith("*")) + { + if(Msg.StartsWith(x.Substring(0, x.Length - 1))) + return true; + continue; + } + + if(x == Msg) + return true; + } + + return false; + } + } + + public class GMCPEchoConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("GMCP.Modules", "gmcp.*", "Which GMCP modules would you like to echo to client. Separate with ',' sign and use '*' for wildcard but the wildcard can only be at the end not in the middle or start. For example \"gmcp.char.*, gmcp.room.*\". Use \"gmcp.*\" to echo everything."); + CreateSetting("GMCP.AuthMask", ulong.MaxValue, "Which client security levels see GMCP echo. This is a 64 bit mask. For example if you want security level 1 and 3 to see GMCP you would enter value 5 (1 + 4). These values are 2 ^ (level - 1), for example security level 3 mask: 2 ^ (3 - 1) = 4. Then you just add these up. Default (all levels): " + ulong.MaxValue); + } + } +} diff --git a/GMCPEcho/GMCPEcho.csproj b/GMCPEcho/GMCPEcho.csproj new file mode 100644 index 0000000..cbc3173 --- /dev/null +++ b/GMCPEcho/GMCPEcho.csproj @@ -0,0 +1,90 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {86B2AA8D-5B03-4128-9C77-9E94F71CFFEE} + Library + Properties + GMCPEcho + GMCPEcho + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/GMCPEcho/Properties/.svn/all-wcprops b/GMCPEcho/Properties/.svn/all-wcprops new file mode 100644 index 0000000..10ef497 --- /dev/null +++ b/GMCPEcho/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 41 +/svn/!svn/ver/2/trunk/GMCPEcho/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/!svn/ver/2/trunk/GMCPEcho/Properties/AssemblyInfo.cs +END diff --git a/GMCPEcho/Properties/.svn/desktop.ini b/GMCPEcho/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/Properties/.svn/dir-prop-base b/GMCPEcho/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/GMCPEcho/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/GMCPEcho/Properties/.svn/entries b/GMCPEcho/Properties/.svn/entries new file mode 100644 index 0000000..3afebd8 --- /dev/null +++ b/GMCPEcho/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/GMCPEcho/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:43.064138Z +df762a418f90d48e08731801bf1106b8 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1428 + diff --git a/GMCPEcho/Properties/.svn/prop-base/desktop.ini b/GMCPEcho/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/Properties/.svn/props/desktop.ini b/GMCPEcho/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/GMCPEcho/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..e66e546 --- /dev/null +++ b/GMCPEcho/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GMCPEcho")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GMCPEcho")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("692b7a44-ef7f-4680-a81c-8f013fe8fb4f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/GMCPEcho/Properties/.svn/text-base/desktop.ini b/GMCPEcho/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/Properties/.svn/tmp/desktop.ini b/GMCPEcho/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/Properties/.svn/tmp/prop-base/desktop.ini b/GMCPEcho/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/Properties/.svn/tmp/props/desktop.ini b/GMCPEcho/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/Properties/.svn/tmp/text-base/desktop.ini b/GMCPEcho/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/Properties/AssemblyInfo.cs b/GMCPEcho/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e66e546 --- /dev/null +++ b/GMCPEcho/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GMCPEcho")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GMCPEcho")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("692b7a44-ef7f-4680-a81c-8f013fe8fb4f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/GMCPEcho/Properties/desktop.ini b/GMCPEcho/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GMCPEcho/desktop.ini b/GMCPEcho/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GMCPEcho/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/.svn/all-wcprops b/GQPredict/.svn/all-wcprops new file mode 100644 index 0000000..7a016fd --- /dev/null +++ b/GQPredict/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 31 +/svn/!svn/ver/2/trunk/GQPredict +END +GQPredict.cs +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/!svn/ver/2/trunk/GQPredict/GQPredict.cs +END +GQPredict.csproj +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2/trunk/GQPredict/GQPredict.csproj +END diff --git a/GQPredict/.svn/desktop.ini b/GQPredict/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/.svn/dir-prop-base b/GQPredict/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/GQPredict/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/GQPredict/.svn/entries b/GQPredict/.svn/entries new file mode 100644 index 0000000..c81150d --- /dev/null +++ b/GQPredict/.svn/entries @@ -0,0 +1,99 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/GQPredict +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +GQPredict.cs +file + + + + +2016-03-25T22:18:43.000137Z +99116d9829ec2e9c0161fbde0f88f370 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +5817 + +Properties +dir + +GQPredict.csproj +file + + + + +2016-03-25T22:18:43.000137Z +ad86ea1e250fa576b1ac563a9d244254 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2721 + diff --git a/GQPredict/.svn/prop-base/desktop.ini b/GQPredict/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/.svn/props/desktop.ini b/GQPredict/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/.svn/text-base/GQPredict.cs.svn-base b/GQPredict/.svn/text-base/GQPredict.cs.svn-base new file mode 100644 index 0000000..647b946 --- /dev/null +++ b/GQPredict/.svn/text-base/GQPredict.cs.svn-base @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore.Scripting; +using ProxyCore.Input; +using ProxyCore.Output; +using ProxyCore; + +namespace GQPredict +{ + public class GQPredict : Plugin + { + public GQPredict() + : base("gqpredict", "Global Quest Predictor") + { + Author = "Duckbat"; + Version = 1; + Description = "Calculates the chance that the next global quest will be for you (or the level you specify). And shows the ranges that will occur and have already occured in a simple list sorted by range. Type gqpredict to see."; + UpdateUrl = "www.duckbat.com/plugins/update.gqpredict.txt"; + Website = "www.duckbat.com/plugins/index.php?t=gqpredict"; + + RegisterCommand("gqpredict", @"(\d+)", Predict, 3); + RegisterTrigger("ranges", @"^ \s*@w(\d+)\s+(\d+)\s+(Yes|No)$", Ranges); + RegisterTrigger("start", "@CFrom Level To Level Already Run?", Start, TriggerFlags.NotRegex); + RegisterTrigger("delimiter", "@W----------- --------- ------------", Delim, TriggerFlags.NotRegex); + RegisterTrigger("level", @"^\$gmcp\.char\.status\.level (\d+)$", Level); + } + + private int nt; + private int forLevel = 1; + private int charLevel = 1; + private int count; + private uint[] ClientMask; + private int Listen = 0; + private readonly SortedDictionary>> gqRanges = new SortedDictionary>>(); + + private bool Predict(InputData i) + { + Listen = 3; + if(i.Arguments.Success) + { + if(!int.TryParse(i.Arguments.Groups[1].Value, out forLevel)) + forLevel = charLevel; + } + else + forLevel = charLevel; + + i.Command = "gq ranges"; + ClientMask = i.ClientMask; + return false; + } + + private bool Ranges(TriggerData t) + { + if(Listen == 0) + return false; + int from; + int to; + if(!int.TryParse(t.Match.Groups[1].Value, out from) || + !int.TryParse(t.Match.Groups[2].Value, out to)) + return false; + + if(!gqRanges.ContainsKey(from)) + gqRanges[from] = new SortedDictionary>(); + if(!gqRanges[from].ContainsKey(to)) + gqRanges[from][to] = new List(); + gqRanges[from][to].Add(t.Match.Groups[3].Length == 3); + if(t.Match.Groups[3].Length != 3) + count++; + if(to == 201) + { + nt++; + if(nt == 3) + { + StringBuilder str = new StringBuilder(); + int i = 0; + int myGQ = 0; + foreach(KeyValuePair>> x in gqRanges) + { + foreach(KeyValuePair> y in x.Value) + { + foreach(bool z in y.Value) + { + if(forLevel < x.Key || forLevel > y.Key || z) + str.Append(z ? "@r" : "@g"); + else + { + str.Append("@G"); + myGQ++; + } + str.Append("(" + string.Format("{0,3}", x.Key) + " - " + string.Format("{0,3}", y.Key) + + ") "); + i++; + if(i == 6) + { + World.Instance.SendMessage(str.ToString(), ClientMask); + i = 0; + str.Remove(0, str.Length); + } + } + } + } + + if(i > 0) + World.Instance.SendMessage(str.ToString(), ClientMask); + + double chance = 0; + if(count > 0 && myGQ > 0) + chance = (double)myGQ / (double)count * 100.0; + World.Instance.SendMessage("@wChance that the next global quest will be for " + (forLevel != charLevel ? ("level " + forLevel) : "your level") + " is @Y" + string.Format("{0:0.00}", chance).Replace(',', '.') + "%@w.", ClientMask); + if(forLevel == charLevel) + World.Instance.SendMessage("@wUse '@Wgqpredict @w' to see from the viewpoint of another level.", ClientMask); + Listen = 0; + } + } + return true; + } + + private bool Level(TriggerData t) + { + int i; + if(int.TryParse(t.Match.Groups[1].Value, out i)) + charLevel = i; + return false; + } + + private bool Start(TriggerData t) + { + nt = 0; + gqRanges.Clear(); + count = 0; + if(Listen > 0) + { + Listen--; + return true; + } + return false; + } + + private bool Delim(TriggerData t) + { + if(Listen > 0) + { + Listen--; + return true; + } + return false; + } + } +} diff --git a/GQPredict/.svn/text-base/GQPredict.csproj.svn-base b/GQPredict/.svn/text-base/GQPredict.csproj.svn-base new file mode 100644 index 0000000..b2623f6 --- /dev/null +++ b/GQPredict/.svn/text-base/GQPredict.csproj.svn-base @@ -0,0 +1,63 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {BE332B80-9B00-4B31-A3D9-15E85B2BEA83} + Library + Properties + GQPredict + GQPredict + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/GQPredict/.svn/text-base/desktop.ini b/GQPredict/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/.svn/tmp/desktop.ini b/GQPredict/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/.svn/tmp/prop-base/desktop.ini b/GQPredict/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/.svn/tmp/props/desktop.ini b/GQPredict/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/.svn/tmp/text-base/desktop.ini b/GQPredict/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/GQPredict.cs b/GQPredict/GQPredict.cs new file mode 100644 index 0000000..647b946 --- /dev/null +++ b/GQPredict/GQPredict.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore.Scripting; +using ProxyCore.Input; +using ProxyCore.Output; +using ProxyCore; + +namespace GQPredict +{ + public class GQPredict : Plugin + { + public GQPredict() + : base("gqpredict", "Global Quest Predictor") + { + Author = "Duckbat"; + Version = 1; + Description = "Calculates the chance that the next global quest will be for you (or the level you specify). And shows the ranges that will occur and have already occured in a simple list sorted by range. Type gqpredict to see."; + UpdateUrl = "www.duckbat.com/plugins/update.gqpredict.txt"; + Website = "www.duckbat.com/plugins/index.php?t=gqpredict"; + + RegisterCommand("gqpredict", @"(\d+)", Predict, 3); + RegisterTrigger("ranges", @"^ \s*@w(\d+)\s+(\d+)\s+(Yes|No)$", Ranges); + RegisterTrigger("start", "@CFrom Level To Level Already Run?", Start, TriggerFlags.NotRegex); + RegisterTrigger("delimiter", "@W----------- --------- ------------", Delim, TriggerFlags.NotRegex); + RegisterTrigger("level", @"^\$gmcp\.char\.status\.level (\d+)$", Level); + } + + private int nt; + private int forLevel = 1; + private int charLevel = 1; + private int count; + private uint[] ClientMask; + private int Listen = 0; + private readonly SortedDictionary>> gqRanges = new SortedDictionary>>(); + + private bool Predict(InputData i) + { + Listen = 3; + if(i.Arguments.Success) + { + if(!int.TryParse(i.Arguments.Groups[1].Value, out forLevel)) + forLevel = charLevel; + } + else + forLevel = charLevel; + + i.Command = "gq ranges"; + ClientMask = i.ClientMask; + return false; + } + + private bool Ranges(TriggerData t) + { + if(Listen == 0) + return false; + int from; + int to; + if(!int.TryParse(t.Match.Groups[1].Value, out from) || + !int.TryParse(t.Match.Groups[2].Value, out to)) + return false; + + if(!gqRanges.ContainsKey(from)) + gqRanges[from] = new SortedDictionary>(); + if(!gqRanges[from].ContainsKey(to)) + gqRanges[from][to] = new List(); + gqRanges[from][to].Add(t.Match.Groups[3].Length == 3); + if(t.Match.Groups[3].Length != 3) + count++; + if(to == 201) + { + nt++; + if(nt == 3) + { + StringBuilder str = new StringBuilder(); + int i = 0; + int myGQ = 0; + foreach(KeyValuePair>> x in gqRanges) + { + foreach(KeyValuePair> y in x.Value) + { + foreach(bool z in y.Value) + { + if(forLevel < x.Key || forLevel > y.Key || z) + str.Append(z ? "@r" : "@g"); + else + { + str.Append("@G"); + myGQ++; + } + str.Append("(" + string.Format("{0,3}", x.Key) + " - " + string.Format("{0,3}", y.Key) + + ") "); + i++; + if(i == 6) + { + World.Instance.SendMessage(str.ToString(), ClientMask); + i = 0; + str.Remove(0, str.Length); + } + } + } + } + + if(i > 0) + World.Instance.SendMessage(str.ToString(), ClientMask); + + double chance = 0; + if(count > 0 && myGQ > 0) + chance = (double)myGQ / (double)count * 100.0; + World.Instance.SendMessage("@wChance that the next global quest will be for " + (forLevel != charLevel ? ("level " + forLevel) : "your level") + " is @Y" + string.Format("{0:0.00}", chance).Replace(',', '.') + "%@w.", ClientMask); + if(forLevel == charLevel) + World.Instance.SendMessage("@wUse '@Wgqpredict @w' to see from the viewpoint of another level.", ClientMask); + Listen = 0; + } + } + return true; + } + + private bool Level(TriggerData t) + { + int i; + if(int.TryParse(t.Match.Groups[1].Value, out i)) + charLevel = i; + return false; + } + + private bool Start(TriggerData t) + { + nt = 0; + gqRanges.Clear(); + count = 0; + if(Listen > 0) + { + Listen--; + return true; + } + return false; + } + + private bool Delim(TriggerData t) + { + if(Listen > 0) + { + Listen--; + return true; + } + return false; + } + } +} diff --git a/GQPredict/GQPredict.csproj b/GQPredict/GQPredict.csproj new file mode 100644 index 0000000..c27449e --- /dev/null +++ b/GQPredict/GQPredict.csproj @@ -0,0 +1,90 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {BE332B80-9B00-4B31-A3D9-15E85B2BEA83} + Library + Properties + GQPredict + GQPredict + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/GQPredict/Properties/.svn/all-wcprops b/GQPredict/Properties/.svn/all-wcprops new file mode 100644 index 0000000..97387e4 --- /dev/null +++ b/GQPredict/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/2/trunk/GQPredict/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/!svn/ver/2/trunk/GQPredict/Properties/AssemblyInfo.cs +END diff --git a/GQPredict/Properties/.svn/desktop.ini b/GQPredict/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/Properties/.svn/dir-prop-base b/GQPredict/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/GQPredict/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/GQPredict/Properties/.svn/entries b/GQPredict/Properties/.svn/entries new file mode 100644 index 0000000..8c7e7b3 --- /dev/null +++ b/GQPredict/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/GQPredict/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:43.000137Z +8de3c977f2718ff572dfd8c126340590 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1430 + diff --git a/GQPredict/Properties/.svn/prop-base/desktop.ini b/GQPredict/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/Properties/.svn/props/desktop.ini b/GQPredict/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/GQPredict/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..fd4407b --- /dev/null +++ b/GQPredict/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GQPredict")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GQPredict")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("9710e6e6-243f-4053-8c8b-4c877d57a05a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/GQPredict/Properties/.svn/text-base/desktop.ini b/GQPredict/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/Properties/.svn/tmp/desktop.ini b/GQPredict/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/Properties/.svn/tmp/prop-base/desktop.ini b/GQPredict/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/Properties/.svn/tmp/props/desktop.ini b/GQPredict/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/Properties/.svn/tmp/text-base/desktop.ini b/GQPredict/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/Properties/AssemblyInfo.cs b/GQPredict/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..fd4407b --- /dev/null +++ b/GQPredict/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GQPredict")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GQPredict")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("9710e6e6-243f-4053-8c8b-4c877d57a05a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/GQPredict/Properties/desktop.ini b/GQPredict/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/GQPredict/desktop.ini b/GQPredict/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/GQPredict/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Jayrock.Json.dll b/Jayrock.Json.dll new file mode 100644 index 0000000000000000000000000000000000000000..eb1a3fa9b596b20e97b48df44378550a30728025 GIT binary patch literal 86016 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 rooms = new List(); + + [DataMember] + internal List Portals = new List(); + + public Room[] Rooms + { + get + { + return rooms.ToArray(); + } + } + + [DataMember] + public int ExplorableRooms + { + get; + internal set; + } + + /// + /// Get all rooms in area with this name (exact, case sensitive). + /// + /// Name of room. + /// + public List GetRooms(string Name) + { + List r = new List(); + foreach(Room x in rooms) + { + if(x.Name == Name) + r.Add(x); + } + + return r; + } + + [DataMember] + internal List Flags = null; + + /// + /// Check if area has a flag. + /// + /// Flag to check for. + /// + public bool HasFlag(string flag) + { + return Flags != null && Flags.Contains(flag.ToLower().Trim()); + } + + /// + /// Add a flag to area. + /// + /// Flag to add. + public void AddFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(Flags == null) + Flags = new List(); + if(!string.IsNullOrEmpty(flag) && !Flags.Contains(flag)) + Flags.Add(flag); + } + + /// + /// Remove a flag from area. + /// + /// Flag to remove. + /// + public bool RemoveFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(Flags != null && Flags.Contains(flag)) + { + Flags.Remove(flag); + return true; + } + return false; + } + + public override string ToString() + { + return "Area '" + (!string.IsNullOrEmpty(Name) ? Name : "NULL") + "' [" + Entry + "]"; + } + } +} diff --git a/Mapper/.svn/text-base/Edit.cs.svn-base b/Mapper/.svn/text-base/Edit.cs.svn-base new file mode 100644 index 0000000..9f71d08 --- /dev/null +++ b/Mapper/.svn/text-base/Edit.cs.svn-base @@ -0,0 +1,497 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore; +using ProxyCore.Input; + +namespace Mapper +{ + public partial class Mapper + { + private bool CommandExit(InputData i) + { + // 1 2 3 4 + // @"^(help)?(room\s+\d+)?(\d+)?(\s+.+)?" + if(i.Arguments.Groups[1].Length != 0) + { + World.Instance.SendMessage("@wSyntax:", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map exit") + " - show exits in current room.", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map exit help") + " - show this message.", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map exit room ") + " - show exits in room.", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map exit [option] [value]") + " - change exit options / show information by exit ID.", i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage("@wAvailable options for exit:", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "addflag") + " @w- Add a flag to exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "removeflag") + " @w- Remove a flag from exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "command") + " @w- Change command that activates exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "doorcommand") + " @w- Change command that we use to open door, for example '@Wopen altar@w'. Use clear to remove this field.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "cost") + " @w- Change cost of using exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "minlevel") + " @w- Minimum level required to use exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "maxlevel") + " @w- Maximum level allowed to use exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "delete confirm") + " @w- Delete the exit.", i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage("@wAvailable default flags:", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "recallmechanic") + " @w- This exit uses recall mechanic, can't use in norecall (for portals).", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "door") + " @w- This exit has a door.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "nogq") + " @w- Don't use this exit on a global quest (for regular chaos portals).", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "disabled") + " @w- Don't use this exit ever.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "nopass") + " @w- The door is nopass.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "locked") + " @w- The door is locked.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "pickable") + " @w- Door can be pick locked or bashdoor.", i.ClientMask); + World.Instance.SendMessage("@wNote: there may be custom flags that are implemented by other plugins / scripts that aren't listed here.", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[2].Length != 0 || (i.Arguments.Groups[2].Length == 0 && i.Arguments.Groups[1].Length == 0 && i.Arguments.Groups[3].Length == 0)) + { + uint roomId; + if(i.Arguments.Groups[2].Length != 0) + { + string str = i.Arguments.Groups[2].Value.Substring(4).Trim(); + if(!uint.TryParse(str, out roomId)) + { + World.Instance.SendMessage("@wInvalid room ID given. Type '@Wmap exit help@w' for syntax.", + i.ClientMask); + return true; + } + } + else + { + roomId = CurrentRoomId; + if(roomId == uint.MaxValue) + { + World.Instance.SendMessage("@wYou are in an unknown room.", i.ClientMask); + return true; + } + } + + Room r = GetRoom(roomId); + if(r == null) + { + World.Instance.SendMessage("@wNo such room in database (@R" + roomId + "@w).", i.ClientMask); + return true; + } + + World.Instance.SendMessage("@wExits in '@G" + r.Name + "@w' [@Y" + r.Entry + "@w]:", i.ClientMask); + if(r.exits.Count == 0) + World.Instance.SendMessage("@wNo exits found.", i.ClientMask); + else + { + World.Instance.SendMessage("@WEntry Command To", i.ClientMask); + World.Instance.SendMessage("@G====== ==================== ========================", i.ClientMask); + foreach(Exit e in r.exits) + World.Instance.SendMessage("@Y" + string.Format("{0,-6}", e.Entry) + " @W" + string.Format("{0,-20}", e.Command) + " @w[@Y" + string.Format("{0,6}", e.To.Entry) + "@w] @G" + e.To.Name, i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage("@wType '@Wmap exit @w' for more information about an exit.", i.ClientMask); + World.Instance.SendMessage("@wOr '@Wmap exit help@w' for syntax.", i.ClientMask); + } + return true; + } + + if(i.Arguments.Groups[3].Length != 0) + { + uint exitId; + if(!uint.TryParse(i.Arguments.Groups[3].Value, out exitId)) + { + World.Instance.SendMessage("@wInvalid exit ID given. Type '@Wmap exit help@w' for syntax.", i.ClientMask); + return true; + } + + Exit e = GetExit(exitId); + if(e == null && IPortals.ContainsKey(exitId)) + e = IPortals[exitId]; + if(e == null) + { + World.Instance.SendMessage("@wNo such exit in database (@R" + exitId + "@w).", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[4].Length != 0) + { + string key, val; + string str = i.Arguments.Groups[4].Value.Trim(); + if(str.Contains(' ')) + { + key = str.Substring(0, str.IndexOf(' ')).ToLower(); + val = str.Substring(key.Length).Trim(); + + switch(key) + { + case "addflag": + e.AddFlag(val); + break; + + case "removeflag": + e.RemoveFlag(val); + break; + + case "command": + val = val.ToLower(); + e.Command = PathfindResult.IsDirectionCommand(val) != 'x' ? + PathfindResult.IsDirectionCommand(val).ToString() : + val; + break; + + case "doorcommand": + val = val.ToLower(); + e.DoorCommand = val != "clear" ? val : null; + break; + + case "cost": + { + uint u; + if(uint.TryParse(val, out u)) + e.Cost = u; + } break; + + case "minlevel": + { + int lvl; + if(int.TryParse(val, out lvl)) + e.MinLevel = lvl; + } break; + + case "maxlevel": + { + int lvl; + if(int.TryParse(val, out lvl)) + e.MaxLevel = lvl; + } break; + + case "delete": + { + if(val == "confirm") + { + if(e.HasFlag("portal")) + { + IPortals.Remove(e.Entry); + e.To.Area.Portals.Remove(e); + } + else + { + IExits.Remove(e.Entry); + e.From.exits.Remove(e); + } + World.Instance.SendMessage("@wDeleted exit or portal (@R" + e.Entry + "@w).", + i.ClientMask); + return true; + } + World.Instance.SendMessage("@wEnter '@Wmap exit delete confirm@w' to remove the exit.", i.ClientMask); + } break; + + default: + World.Instance.SendMessage("@wInvalid key value pair entered '@R" + key + " " + val + "@w'.", i.ClientMask); + World.Instance.SendMessage("@wUse '@Wmap exit help@w' to see syntax.", i.ClientMask); + break; + } + } + } + + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + World.Instance.SendMessage("@w| @WEntry @w: @Y" + string.Format("{0,-55}", e.Entry) + "@w|", i.ClientMask); + if(!e.HasFlag("portal")) + World.Instance.SendMessage("@w| @WFrom @w: @w[@Y" + string.Format("{0,6}", e.From.Entry) + "@w] @G" + Utility.FormatColoredString(!string.IsNullOrEmpty(e.From.Name) ? e.From.Name : "Unknown", -46) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WTo @w: @w[@Y" + string.Format("{0,6}", e.To.Entry) + "@w] @G" + Utility.FormatColoredString(!string.IsNullOrEmpty(e.To.Name) ? e.To.Name : "Unknown", -46) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WCommand @w: @c" + string.Format("{0,-55}", e.Command) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WDoor command@w: @c" + string.Format("{0,-55}", !string.IsNullOrEmpty(e.DoorCommand) ? e.DoorCommand : "none") + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WCost @w: @C" + string.Format("{0,-55}", e.Cost) + "@w|", i.ClientMask); + StringBuilder strFlags = new StringBuilder(); + if(e.IFlags != null) + { + foreach(string x in e.IFlags) + { + if(strFlags.Length > 0) + strFlags.Append(", "); + strFlags.Append(x); + } + } + + string[] spl = Utility.WrapColored(strFlags.ToString(), 54, 0); + for(int j = 0; j < spl.Length; j++) + { + if(j == 0) + World.Instance.SendMessage("@w| @WFlags @w: " + string.Format("{0,-55}", spl[j]) + "@w|", i.ClientMask); + else + World.Instance.SendMessage("@w| : " + string.Format("{0,-55}", spl[j]) + "@w|", i.ClientMask); + } + + World.Instance.SendMessage("@w| @WMin level @w: @W" + string.Format("{0,-55}", e.MinLevel) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WMax level @w: @W" + string.Format("{0,-55}", e.MaxLevel) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + if(i.Arguments.Groups[4].Length == 0) + World.Instance.SendMessage("@wSee '@Wmap exit help@w' for information on how to edit this exit.", i.ClientMask); + return true; + } + + World.Instance.SendMessage("0", i.ClientMask); + return true; + } + + private bool CommandPortal(InputData i) + { + if(IPortals.Count == 0) + { + World.Instance.SendMessage("@wYou have no portals set.", i.ClientMask); + return true; + } + + int count = 0; + World.Instance.SendMessage("@WEntry Command To", i.ClientMask); + World.Instance.SendMessage("@G======== ================ =====================================", i.ClientMask); + foreach(KeyValuePair x in IPortals) + { + World.Instance.SendMessage("@w[@Y" + string.Format("{0,6}", x.Key) + "@w] @w" + string.Format("{0,-16}", x.Value.Command) + " @M" + x.Value.To.Area.Name + " @wroom [@Y" + x.Value.To.Entry + "@w]", i.ClientMask); + count++; + } + + World.Instance.SendMessage("@C" + count + " @wportals found.", i.ClientMask); + return true; + } + + private bool CommandCreateExit(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: map createexit [from room ID = current] \"\"", i.ClientMask); + return true; + } + + uint fromId; + uint toId; + if(i.Arguments.Groups[2].Length > 0) + { + if(!uint.TryParse(i.Arguments.Groups[1].Value, out fromId) || + !uint.TryParse(i.Arguments.Groups[2].Value.Trim(), out toId)) + { + World.Instance.SendMessage("@wSyntax: map createexit [from room ID = current] \"\"", i.ClientMask); + return true; + } + } + else + { + fromId = CurrentRoomId; + if(!uint.TryParse(i.Arguments.Groups[1].Value, out toId)) + { + World.Instance.SendMessage("@wSyntax: map createexit [from room ID = current] \"\"", i.ClientMask); + return true; + } + } + + Room from = GetRoom(fromId); + Room to = GetRoom(toId); + if(from == null || to == null) + { + World.Instance.SendMessage("@wNo such room exists in mapper database.", i.ClientMask); + return true; + } + + char dir = PathfindResult.IsDirectionCommand(i.Arguments.Groups[3].Value); + Exit e = new Exit(++_guidExit); + e.Command = dir != 'x' ? char.ToLower(dir).ToString() : i.Arguments.Groups[3].Value.ToLower(); + e.From = from; + e.To = to; + e.From.exits.Add(e); + e.From.UpdateExits(); + IExits[e.Entry] = e; + World.Instance.SendMessage("@wCreated a new exit (@R" + e.Entry + "@w).", i.ClientMask); + World.Instance.SendMessage("@wType '@Wmap exit " + e.Entry + "@w' for more information or to edit.", i.ClientMask); + return true; + } + + private bool CommandCreatePortal(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: map createportal \"\"", i.ClientMask); + return true; + } + + uint toId; + if(!uint.TryParse(i.Arguments.Groups[1].Value, out toId)) + { + World.Instance.SendMessage("@wSyntax: map createportal \"\"", i.ClientMask); + return true; + } + + Room to = GetRoom(toId); + if(to == null) + { + World.Instance.SendMessage("@wNo such room exists in mapper database.", i.ClientMask); + return true; + } + + Exit e = new Exit(++_guidExit); + e.Command = i.Arguments.Groups[2].Value.ToLower(); + e.To = to; + e.Cost = 5; + if(e.IFlags == null) + e.IFlags = new List(); + e.IFlags.Add("portal"); + IPortals[e.Entry] = e; + to.Area.Portals.Add(e); + World.Instance.SendMessage("@wCreated a new portal (@R" + e.Entry + "@w).", i.ClientMask); + World.Instance.SendMessage("@wType '@Wmap exit " + e.Entry + "@w' for more information or to edit.", i.ClientMask); + return true; + } + + private bool CommandRoomInfo(InputData i) + { + // 1 2 3 + // @"(help)?(\d+)?(\s+.+)?" + if(i.Arguments.Success && i.Arguments.Groups[1].Length != 0) + { + World.Instance.SendMessage("@wSyntax:", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map room") + " - show info about current room.", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map room help") + " - show this message.", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map room [option] [value]") + " - show info about room by ID or change it.", i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage("@wAvailable options for room:", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "addflag") + " @w- Add a flag to room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "removeflag") + " @w- Remove a flag from room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "addcflag") + " @w- Add a custom flag to room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "removecflag") + " @w- Remove a custom flag from room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "name") + " @w- Change name of room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "entrycost") + " @w- Change cost of entering room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "leavecost") + " @w- Change cost of leaving room.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "healrate") + " @w- Change heal rate.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "manarate") + " @w- Change mana rate.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "sector") + " @w- Change sector.", i.ClientMask); + return true; + } + + uint roomId; + if(i.Arguments.Success && i.Arguments.Groups[2].Length != 0) + { + if(!uint.TryParse(i.Arguments.Groups[2].Value, out roomId)) + { + World.Instance.SendMessage("@wInvalid room ID given (@R" + i.Arguments.Groups[2].Value + "@w).", i.ClientMask); + World.Instance.SendMessage("@wUse '@Wmap roominfo help@w' to see syntax.", i.ClientMask); + return true; + } + } + else + { + roomId = CurrentRoomId; + if(roomId == uint.MaxValue) + { + World.Instance.SendMessage("@wYou are in an invalid room.", i.ClientMask); + World.Instance.SendMessage("@wUse '@Wmap roominfo help@w' to see syntax.", i.ClientMask); + return true; + } + } + + Room r = GetRoom(roomId); + if(r == null) + { + World.Instance.SendMessage("@wNo such room in database (@R" + roomId + "@w).", i.ClientMask); + return true; + } + + if(i.Arguments.Success && i.Arguments.Groups[2].Length != 0 && i.Arguments.Groups[3].Length != 0) + { + string key, val; + string str = i.Arguments.Groups[3].Value.Trim(); + if(str.Contains(' ')) + { + key = str.Substring(0, str.IndexOf(' ')).ToLower(); + val = str.Substring(key.Length).Trim(); + + switch(key) + { + case "addflag": + r.AddFlag(val); + break; + + case "removeflag": + r.RemoveFlag(val); + break; + + case "addcflag": + r.AddCustomFlag(val); + break; + + case "removecflag": + r.RemoveCustomFlag(val); + break; + + case "name": + r.Name = val; + break; + + case "entrycost": + { + uint u; + if(uint.TryParse(val, out u)) + r.EntryCost = u; + } break; + + case "leavecost": + { + uint u; + if(uint.TryParse(val, out u)) + r.LeaveCost = u; + } break; + } + } + } + + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + World.Instance.SendMessage("@w| @WEntry @w: @Y" + string.Format("{0,-55}", r.Entry) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WName @w: @G" + Utility.FormatColoredString(!string.IsNullOrEmpty(r.Name) ? r.Name : "Unknown", -55) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WArea @w: @M" + string.Format("{0,-55}", !string.IsNullOrEmpty(r.Area.Name) ? r.Area.Name : "Unknown") + "@w|", i.ClientMask); + { + StringBuilder strFlags = new StringBuilder(); + if(r.IFlags != null) + { + foreach(string x in r.IFlags) + { + if(strFlags.Length > 0) + strFlags.Append(", "); + strFlags.Append(x); + } + } + + string[] spl = Utility.WrapColored(strFlags.ToString(), 54, 0); + for(int j = 0; j < spl.Length; j++) + { + if(j == 0) + World.Instance.SendMessage("@w| @WFlags @w: " + string.Format("{0,-55}", spl[j]) + "@w|", + i.ClientMask); + else + World.Instance.SendMessage("@w| : " + string.Format("{0,-55}", spl[j]) + "@w|", + i.ClientMask); + } + } + + { + StringBuilder strFlags = new StringBuilder(); + if(r.CFlags != null) + { + foreach(string x in r.CFlags) + { + if(strFlags.Length > 0) + strFlags.Append(", "); + strFlags.Append(x); + } + } + + string[] spl = Utility.WrapColored(strFlags.ToString(), 54, 0); + for(int j = 0; j < spl.Length; j++) + { + if(j == 0) + World.Instance.SendMessage("@w| @WCustom flags@w: " + string.Format("{0,-55}", spl[j]) + "@w|", i.ClientMask); + else + World.Instance.SendMessage("@w| : " + string.Format("{0,-55}", spl[j]) + "@w|", i.ClientMask); + } + } + + // Healrate, manarate, sector + + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + if(!i.Arguments.Success || (i.Arguments.Groups[1].Length == 0 && i.Arguments.Groups[2].Length == 0 && i.Arguments.Groups[3].Length == 0)) + World.Instance.SendMessage("@wUse '@Wmap roominfo help@w' to see syntax.", i.ClientMask); + return true; + } + } +} diff --git a/Mapper/.svn/text-base/Exit.cs.svn-base b/Mapper/.svn/text-base/Exit.cs.svn-base new file mode 100644 index 0000000..e36b754 --- /dev/null +++ b/Mapper/.svn/text-base/Exit.cs.svn-base @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; + +namespace Mapper +{ + [DataContract] + public class Exit + { + public Exit(uint entry) + { + Entry = entry; + MinLevel = 0; + MaxLevel = 210; + Cost = 1; + } + + [DataMember] + public readonly uint Entry; + + [DataMember] + public int MinLevel + { + get; + internal set; + } + + [DataMember] + public int MaxLevel + { + get; + internal set; + } + + public Room From + { + get; + internal set; + } + + public Room To + { + get + { + return _to; + } + internal set + { + _to = value; + ToRoom = value != null ? value.Entry : uint.MaxValue; + } + } + + private Room _to; + + [DataMember] + internal uint ToRoom; + + [DataMember] + public string Command + { + get; + internal set; + } + + [DataMember] + public string DoorCommand + { + get; + internal set; + } + + [DataMember] + internal List IFlags = null; + + public IEnumerable Flags + { + get + { + return IFlags; + } + } + + [DataMember] + public uint Cost + { + get; + internal set; + } + + /// + /// Check if exit has a flag. + /// + /// Flag to check for. + /// + public bool HasFlag(string flag) + { + return IFlags != null && IFlags.Contains(flag.ToLower().Trim()); + } + + /// + /// Add a flag to exit. + /// + /// Flag to add. + public void AddFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(flag == "portal") + return; + if(IFlags == null) + IFlags = new List(); + if(!string.IsNullOrEmpty(flag) && !IFlags.Contains(flag)) + IFlags.Add(flag); + } + + /// + /// Remove a flag from exit. + /// + /// Flag to remove. + /// + public bool RemoveFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(flag == "portal") + return false; + if(IFlags != null && IFlags.Contains(flag)) + { + IFlags.Remove(flag); + return true; + } + return false; + } + } +} diff --git a/Mapper/.svn/text-base/Mapper.cs.svn-base b/Mapper/.svn/text-base/Mapper.cs.svn-base new file mode 100644 index 0000000..40a8d72 --- /dev/null +++ b/Mapper/.svn/text-base/Mapper.cs.svn-base @@ -0,0 +1,1607 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Mapper.Scripting; +using ProxyCore; +using ProxyCore.Input; +using ProxyCore.Output; +using ProxyCore.Scripting; +using System.IO; +using System.Runtime.Serialization; + +namespace Mapper +{ + public partial class Mapper : Plugin + { + public Mapper() + : base("mapper", "Mapper") + { + Author = "Duckbat"; + Version = 15; + Description = "This plugin will record what rooms you have been in and from that can generate speedwalks for you to easily move around the world of Aardwolf."; + UpdateUrl = "www.duckbat.com/plugins/update.mapper.txt"; + Website = "code.google.com/p/proxymud/"; + + Config = new MapperConfig(); + + RegisterCommand("map", "", CommandMapper); + RegisterCommand("all", @"(.+)", CommandAll, 0, CMDFlags.None, "map"); + RegisterCommand("count", "", CommandCount, 0, CMDFlags.None, "map"); + RegisterCommand("createexit", "^(\\d+)(\\s+\\d+)?\\s+\"(.+)\"$", CommandCreateExit, 0, CMDFlags.None, "map"); + RegisterCommand("createportal", "^(\\d+)\\s+\"(.+)\"$", CommandCreatePortal, 0, CMDFlags.None, "map"); + RegisterCommand("delete", @"^(area|room|exit|portal)\s+(\d+)", CommandDelete, 3, CMDFlags.None, "map"); + RegisterCommand("exits", @"^(help)?(room\s+\d+)?(\d+)?(\s+.+)?", CommandExit, 4, CMDFlags.None, "map"); + RegisterCommand("find", @"^(room|area)(\s+exact)?(\s+case)?\s+(.+)", CommandFind, 1, CMDFlags.None, "map"); + RegisterCommand("portals", "", CommandPortal, 4, CMDFlags.None, "map"); + RegisterCommand("goto", @"(.+)", CommandGoto, 2, CMDFlags.None, "map"); + RegisterCommand("import", @"(.+)", CommandImport, 0, CMDFlags.None, "map"); + RegisterCommand("roominfo", @"^(help)?(\d+)?(\s+.+)?", CommandRoomInfo, 4, CMDFlags.None, "map"); + RegisterCommand("save", @"(.+)", CommandSave, 4, CMDFlags.None, "map"); + RegisterCommand("unmapped", @"^(go|all)$", CommandUnmapped, 2, CMDFlags.None, "map"); + RegisterCommand("unreconed", @"^(go|all)$", CommandUnreconed, 3, CMDFlags.None, "map"); + + RegisterTrigger("room.id", @"^\$gmcp\.room\.info\.num (-?\d+)$", TriggerRoomInfoNum); + RegisterTrigger("room.name", @"^\$gmcp\.room\.info\.name (.*)$", TriggerRoomInfoName); + RegisterTrigger("room.area", @"^\$gmcp\.room\.info\.zone (.*)$", TriggerRoomInfoArea); + RegisterTrigger("room.exit", @"^\$gmcp\.room\.info\.exits\.(\w) (-?\d+)$", TriggerRoomInfoExits); + RegisterTrigger("room.finish", @"^\$gmcp\.room\.info\.coords?\.", TriggerRoomInfoFinish); + RegisterTrigger("char.level", @"^\$gmcp\.char\.status\.level (\d+)$", TriggerCharStatusLevel); + RegisterTrigger("char.tier", @"^\$gmcp\.char\.base\.tier (\d+)$", TriggerCharBaseTier); + RegisterTrigger("char.remorts", @"^\$gmcp\.char\.base\.remorts (\d+)$", TriggerCharBaseRemorts); + RegisterTrigger("gq.join", @"@wYou have now joined the quest. See 'help gquest' for available commands.", + TriggerJoinedGQ, TriggerFlags.NotRegex); + RegisterTrigger("gq.left", @"@wYou are no longer part of the current quest.", TriggerLeftGQ, + TriggerFlags.NotRegex); + RegisterTrigger("gq.win", + @"^@RGlobal Quest@Y: @wThe global quest has been won by @Y\w+ @w- @Y\d+.. @wwin\.$", + TriggerLeftGQ); + RegisterTrigger("gq.quit", @"@wYou are no longer part of the current quest.", TriggerLeftGQ, TriggerFlags.NotRegex); + RegisterTrigger("where.name", @"^@GYou are in area : (.+)", TriggerWhereName); + RegisterTrigger("where.level", @"^@GLevel range is : @R(\d+) to (\d+)", TriggerWhereLevel); + RegisterTrigger("areas.start", @"^@WFrom To Lock Keyword Area Name", TriggerAreasStart); + RegisterTrigger("areas.end", @"@w---------------------------------------------------------------", TriggerAreasEnd, TriggerFlags.NotRegex); + RegisterTrigger("areas.entry", @"^\s+@w(\d+)\s+(\d+)\s+(@R\d+\s+)?@g([\d\w]+)\s+@c(.+)", TriggerAreasEntry); + RegisterTrigger("home", @"@wYou cannot return home from this room.", TriggerNoRecall, TriggerFlags.NotRegex); + RegisterTrigger("recall", @"@wYou cannot recall from this room.", TriggerNoRecall, TriggerFlags.NotRegex); + RegisterTrigger("portal", @"@wMagic walls bounce you back.", TriggerPrison, TriggerFlags.NotRegex); + RegisterTrigger("recon.area", @"^@wArea Name : (.+)", TriggerReconArea); + RegisterTrigger("recon.sector", @"^@wSector type is : (.+)", TriggerReconSector); + RegisterTrigger("recon.flags", @"^@wBase flags :(.*)", TriggerReconFlags); + RegisterTrigger("recon.healrate", @"^@wHeal rate : @c(-?\d+)", TriggerReconHealRate); + RegisterTrigger("recon.manarate", @"^@wMana rate : @c(-?\d+)", TriggerReconManaRate); + RegisterTrigger("recon.unable", "@wYou cannot perform reconnaissance in this room.", TriggerNoRecon, TriggerFlags.NotRegex); + RegisterTrigger("tags.exits", @"^(@w)?\{exits\}(.*)", TriggerTagsExits); + + Load(); + + // If we don't have this unmapped area set then create it, just so we can save rooms we haven't explored yet + if(GetArea(uint.MaxValue) == null) + { + Area a = new Area(uint.MaxValue); + a.Keyword = "mapper_unmapped"; + a.Name = "Mapper unmapped rooms"; + IAreas[a.Entry] = a; + } + + Path_Init(); + } + + private const string DBFileName = "mapperdb.xml"; + private const string DBFileBackup = "mapperdb_backup.xml"; + + private int Level = 1; + private int Tier = 0; + private bool ListenArea = false; + private int Remorts = 1; + private bool HasGQ = false; + private long WhenSave = 0; + + private void OnDeleted(Room r) + { + List del = new List(); + foreach(KeyValuePair x in IExits) + { + if(x.Key == uint.MaxValue || x.Key == 0) + continue; + if(!IRooms.ContainsKey(x.Value.ToRoom)) + del.Add(x.Key); + } + + foreach(uint x in del) + { + IExits[x].From.exits.Remove(IExits[x]); + IExits.Remove(x); + } + } + + /// + /// Fill a pathfinder with our current character values and return if we were successful in doing that. + /// + /// + public bool FillPathFinder(Pathfinder p) + { + Room r; + if(CurrentRoomId == uint.MaxValue || (r = GetRoom(CurrentRoomId)) == null || r.Area.Entry == uint.MaxValue) + return false; + p.CharacterLevel = Level; + p.CharacterTier = Tier; + p.CanUseRecalls = true; + p.CanUsePortals = true; + p.IsGlobalQuest = HasGQ; + p.IsSingleClassTier0 = Tier == 0 && Remorts == 1; + p.StartRooms = new[] { r }; + return true; + } + + #region Commands + private bool CommandAll(InputData i) + { + string[] spl; + if(!i.Arguments.Success || (spl = i.Arguments.Groups[1].Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)).Length == 0) + { + World.Instance.SendMessage("@wSyntax: map all [room id] ...", i.ClientMask); + return true; + } + + List rooms = new List(); + bool hadConfirm = false; + foreach(string x in spl) + { + uint u; + if(uint.TryParse(x, out u)) + { + Room r = GetRoom(u); + if(r != null && !rooms.Contains(r)) + rooms.Add(r); + } + else if(x.ToLower() == "confirm") + hadConfirm = true; + } + + if(rooms.Count < 2) + { + World.Instance.SendMessage("@wSyntax: map all [room id] ...", i.ClientMask); + return true; + } + + if(rooms.Count > 10 && !hadConfirm) + { + World.Instance.SendMessage("@RWarning! @wEntered more than 10 rooms. This may be too slow to finish. Enter confirm at the end if you are sure you wish to do this pathfind.", i.ClientMask); + return true; + } + + int ms = Environment.TickCount; + PathFinder_VisitAll pf = new PathFinder_VisitAll(this, rooms.ToArray()); + FillPathFinder(pf); + PathfindResult pr = Get(pf); + ms = Environment.TickCount - ms; + + World.Instance.SendMessage("@wPathfind took @W" + ms + " @wms.", i.ClientMask); + if(!pr.Success) + { + World.Instance.SendMessage("@wPathfind failed.", i.ClientMask); + return true; + } + + string sw = PathfindResult.Speedwalk(pr.Path); + World.Instance.SendMessage("@wCost: @W" + pr.Cost, i.ClientMask); + World.Instance.SendMessage("@wSW: " + sw, i.ClientMask); + return true; + } + + private bool CommandImport(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: map import ", i.ClientMask); + return true; + } + + StreamReader f; + try + { + f = new StreamReader(i.Arguments.Groups[1].Value); + } + catch + { + World.Instance.SendMessage("@wFailed opening file. Make sure it is in the same folder as ProxyMud.exe and make sure you entered the file extension too.", i.ClientMask); + return true; + } + + string l; + while((l = f.ReadLine()) != null) + { + if(l.StartsWith("{area}")) + { + l = l.Substring(6); + string key = l.Substring(0, l.IndexOf('\t')).Trim().ToLower(); + l = l.Substring(l.IndexOf('\t') + 1); + + if(string.IsNullOrEmpty(key)) + continue; + + if(GetArea(key) == null) + { + Area a = new Area(++_guidArea); + a.Name = l; + a.Keyword = key; + IAreas[a.Entry] = a; + } + else if(string.IsNullOrEmpty(GetArea(key).Name)) + GetArea(key).Name = l; + } + else if(l.StartsWith("{room}")) + { + l = l.Substring(6); + string key = l.Substring(0, l.IndexOf('\t')); + uint k; + if(!uint.TryParse(key, out k)) + continue; + + l = l.Substring(l.IndexOf('\t') + 1); + string name = l.Substring(0, l.IndexOf('\t')); + + l = l.Substring(l.IndexOf('\t') + 1).Trim().ToLower(); + Area a = GetArea(l); + if(a == null) + { + a = new Area(++_guidArea); + a.Keyword = l; + IAreas[a.Entry] = a; + } + + Room r = GetRoom(k); + if(r == null) + { + r = new Room(k); + IRooms[k] = r; + r.Name = name; + r.Area = a; + } + else + { + if(r.Area != a) + r.Area = a; + r.Name = name; + } + } + else if(l.StartsWith("{exit}")) + { + l = l.Substring(6); + string cmd = l.Substring(0, l.IndexOf('\t')); + l = l.Substring(l.IndexOf('\t') + 1); + + string fr = l.Substring(0, l.IndexOf('\t')); + l = l.Substring(l.IndexOf('\t') + 1); + + string tr = l.Substring(0, l.IndexOf('\t')); + l = l.Substring(l.IndexOf('\t') + 1); + + uint from; + uint to; + int minLevel; + + if(!uint.TryParse(fr, out from) || !uint.TryParse(tr, out to) || !int.TryParse(l, out minLevel)) + continue; + + Room fromroom = GetRoom(from); + Room toroom = GetRoom(to); + if(fromroom == null || toroom == null) + continue; + + string door = ""; + if(cmd.Contains(';')) + { + door = cmd.Substring(0, cmd.IndexOf(';')); + if(!door.StartsWith("open ") && !door.StartsWith("ope ") && !door.StartsWith("op ") && + !door.StartsWith("o ")) + door = ""; + else + { + door = door.Substring(door.IndexOf(' ') + 1).Trim(); + char dir = PathfindResult.IsDirectionCommand(door); + if(dir == 'x') + door = "open " + door; + else + door = "o"; + cmd = cmd.Substring(cmd.IndexOf(';') + 1); + } + } + + Exit e = null; + if(PathfindResult.IsDirectionCommand(cmd) != 'x') + e = fromroom.GetExit(PathfindResult.IsDirectionCommand(cmd)); + else + e = fromroom.GetExit(cmd); + if(e == null) + { + e = new Exit(++_guidExit); + e.Command = PathfindResult.IsDirectionCommand(cmd) != 'x' ? PathfindResult.IsDirectionCommand(cmd).ToString() : cmd; + e.From = fromroom; + e.To = toroom; + e.From.exits.Add(e); + e.From.UpdateExits(); + IExits[e.Entry] = e; + + if(door == "o") + e.AddFlag("door"); + else if(!string.IsNullOrEmpty(door)) + e.DoorCommand = door; + } + } + else if(l.Length == 0) + continue; + else + { + f.Close(); + World.Instance.SendMessage("@wInvalid mapper format. You must convert it to readable format with the converter.", i.ClientMask); + return true; + } + } + + f.Close(); + World.Instance.SendMessage("@wDone importing missing rooms / areas / exits from MUSH database.", i.ClientMask); + return true; + } + + private bool CommandSave(InputData i) + { + string fileName = DBFileName; + if(i.Arguments.Success) + fileName = i.Arguments.Groups[0].Value; + + Save(fileName); + World.Instance.SendMessage("@wSaved mapper database to '@W" + fileName + "@w'.", i.ClientMask); + return true; + } + + private bool CommandUnmapped(InputData i) + { + if(!i.Arguments.Success) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + { + World.Instance.SendMessage("@wYou are in an unkown room.", i.ClientMask); + return true; + } + + uint c = 0; + foreach(KeyValuePair x in IRooms) + { + if(x.Value.Area.Entry != r.Area.Entry) + continue; + + bool f = false; + foreach(Exit e in x.Value.exits) + { + if(e.To.Area.Entry == uint.MaxValue) + { + f = true; + break; + } + } + + if(f) + c++; + } + + if(c == 0) + { + World.Instance.SendMessage("@wDidn't find any rooms with exits to unmapped rooms in this area.", + i.ClientMask); + } + else + { + World.Instance.SendMessage( + "@wFound @C" + c + " @wroom" + (c != 1 ? "s" : "") + + " in this area that have exits to unmapped rooms.", i.ClientMask); + } + World.Instance.SendMessage("@wUse '@Wmap unmapped all@w' to see areas where exits to unmapped rooms are found.", i.ClientMask); + World.Instance.SendMessage("@wUse '@Wmap unmapped go@w' to go to the closest room where an exit to unmapped room is found (in current area only).", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[1].Value.ToLower() == "all") + { + SortedDictionary> Unmapped = new SortedDictionary>(); + foreach(KeyValuePair x in IRooms) + { + bool f = false; + foreach(Exit e in x.Value.exits) + { + if(e.To.Area.Entry == uint.MaxValue) + { + f = true; + break; + } + } + + if(f) + { + string n = x.Value.Area.Name; + if(string.IsNullOrEmpty(n)) + n = x.Value.Area.Keyword; + if(!Unmapped.ContainsKey(n)) + Unmapped[n] = new Dictionary(); + if(!Unmapped[n].ContainsKey(x.Value.Area)) + Unmapped[n][x.Value.Area] = 1; + else + Unmapped[n][x.Value.Area]++; + } + } + + if(Unmapped.Count == 0) + World.Instance.SendMessage("@wDidn't find any rooms in any area that have exits to unmapped rooms.", i.ClientMask); + else + { + World.Instance.SendMessage("@WEntry Area Unmapped rooms", i.ClientMask); + World.Instance.SendMessage("@G===== ============================================ ==============", i.ClientMask); + int c = 0; + foreach(KeyValuePair> x in Unmapped) + { + foreach(KeyValuePair y in x.Value) + { + c++; + World.Instance.SendMessage("@Y" + string.Format("{0,-5}", y.Key.Entry) + " @M" + string.Format("{0,-" + "============================================".Length + "}", (!string.IsNullOrEmpty(y.Key.Name) ? y.Key.Name : y.Key.Keyword)) + " @C" + y.Value, i.ClientMask); + } + } + + World.Instance.SendMessage("@wFound @C" + c + " @warea" + (c != 1 ? "s" : "") + " with exits to unmapped rooms.", i.ClientMask); + } + return true; + } + + if(GetRoom(CurrentRoomId) == null) + { + World.Instance.SendMessage("@wYou are in an invalid room.", i.ClientMask); + return true; + } + + Pathfinder_Unmapped p = new Pathfinder_Unmapped(false); + FillPathFinder(p); + PathfindResult pr = Get(p); + if(!pr.Success) + { + World.Instance.SendMessage("@wCouldn't find a path or there weren't any rooms in this area with exits that lead to unmapped rooms.", i.ClientMask); + return true; + } + Goto(pr); + return true; + } + + private bool CommandUnreconed(InputData i) + { + if(!i.Arguments.Success) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + { + World.Instance.SendMessage("@wYou are in an unknown room.", i.ClientMask); + return true; + } + + uint c = 0; + foreach(KeyValuePair x in IRooms) + { + if(x.Value.Area.Entry != r.Area.Entry) + continue; + + bool f = false; + foreach(Exit e in x.Value.exits) + { + if(e.To.Area.Entry == uint.MaxValue) + { + f = true; + break; + } + } + if(!f && !x.Value.HasCustomFlag("reconed")) + f = true; + + if(f) + c++; + } + + if(c == 0) + { + World.Instance.SendMessage("@wDidn't find any rooms with exits to unmapped rooms or unreconed in this area.", + i.ClientMask); + } + else + { + World.Instance.SendMessage( + "@wFound @C" + c + " @wroom" + (c != 1 ? "s" : "") + + " in this area that have exits to unmapped rooms or aren't reconed.", i.ClientMask); + } + World.Instance.SendMessage("@wUse '@Wmap unreconed all@w' to see areas where exits to unmapped rooms or rooms that aren't reconed are found.", i.ClientMask); + World.Instance.SendMessage("@wUse '@Wmap unreconed go@w' to go to the closest room where an exit to unmapped room is found or that isn't reconed (in current area only).", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[1].Value.ToLower() == "all") + { + SortedDictionary> Unmapped = new SortedDictionary>(); + foreach(KeyValuePair x in IRooms) + { + bool f = false; + foreach(Exit e in x.Value.exits) + { + if(e.To.Area.Entry == uint.MaxValue) + { + f = true; + break; + } + } + if(!f && !x.Value.HasCustomFlag("reconed")) + f = true; + + if(f) + { + string n = x.Value.Area.Name; + if(string.IsNullOrEmpty(n)) + n = x.Value.Area.Keyword; + if(!Unmapped.ContainsKey(n)) + Unmapped[n] = new Dictionary(); + if(!Unmapped[n].ContainsKey(x.Value.Area)) + Unmapped[n][x.Value.Area] = 1; + else + Unmapped[n][x.Value.Area]++; + } + } + + if(Unmapped.Count == 0) + World.Instance.SendMessage("@wDidn't find any rooms in any area that have exits to unmapped rooms or that aren't reconed.", i.ClientMask); + else + { + World.Instance.SendMessage("@WEntry Area Unmapped/Unreconed rooms", i.ClientMask); + World.Instance.SendMessage("@G===== ============================================ ========================", i.ClientMask); + int c = 0; + foreach(KeyValuePair> x in Unmapped) + { + foreach(KeyValuePair y in x.Value) + { + c++; + World.Instance.SendMessage("@Y" + string.Format("{0,-5}", y.Key.Entry) + " @M" + string.Format("{0,-" + "============================================".Length + "}", (!string.IsNullOrEmpty(y.Key.Name) ? y.Key.Name : y.Key.Keyword)) + " @C" + y.Value, i.ClientMask); + } + } + + World.Instance.SendMessage("@wFound @C" + c + " @warea" + (c != 1 ? "s" : "") + " with exits to unmapped rooms or unreconed rooms.", i.ClientMask); + } + return true; + } + + if(GetRoom(CurrentRoomId) == null) + { + World.Instance.SendMessage("@wYou are in an invalid room.", i.ClientMask); + return true; + } + + Pathfinder_Unmapped p = new Pathfinder_Unmapped(true); + FillPathFinder(p); + PathfindResult pr = Get(p); + if(!pr.Success) + { + World.Instance.SendMessage("@wCouldn't find a path or there weren't any rooms in this area with exits that lead to unmapped rooms or that aren't reconed.", i.ClientMask); + return true; + } + Goto(pr); + return true; + } + + private bool CommandDelete(InputData i) + { + // @"^(area|room|exit|portal)\s+(\d+)" + uint id; + if(!i.Arguments.Success || !uint.TryParse(i.Arguments.Groups[2].Value, out id) || id == uint.MaxValue) + { + World.Instance.SendMessage("@wSyntax: map delete area ", i.ClientMask); + World.Instance.SendMessage(" @wmap delete exit ", i.ClientMask); + World.Instance.SendMessage(" @wmap delete portal ", i.ClientMask); + World.Instance.SendMessage(" @wmap delete room ", i.ClientMask); + return true; + } + + string w = i.Arguments.Groups[1].Value.ToLower(); + + switch(w) + { + case "area": + { + Area a = GetArea(id); + if(a == null) + { + World.Instance.SendMessage("@wNo such area (@R" + id + "@w).", i.ClientMask); + return true; + } + + IAreas.Remove(a.Entry); + foreach(Room r in a.rooms) + { + IRooms.Remove(r.Entry); + foreach(Exit e in r.exits) + IExits.Remove(e.Entry); + OnDeleted(r); + } + + if(!string.IsNullOrEmpty(a.Name)) + World.Instance.SendMessage("@wDeleted area '@M" + a.Name + "@w'.", i.ClientMask); + else + World.Instance.SendMessage("@wDeleted area '@w" + a.Keyword + "@w'.", i.ClientMask); + } break; + + case "exit": + case "portal": + { + Exit e = GetExit(id); + if(e == null) + e = IPortals.ContainsKey(id) ? IPortals[id] : null; + if(e == null) + { + World.Instance.SendMessage("@wNo such exit or portal (@R" + id + "@w).", i.ClientMask); + return true; + } + + IExits.Remove(e.Entry); + IPortals.Remove(e.Entry); + if(!e.HasFlag("portal")) + e.From.exits.Remove(e); + else + e.To.Area.Portals.Remove(e); + + World.Instance.SendMessage("@wDeleted exit '@Y" + e.Entry + "@w'.", i.ClientMask); + } break; + + case "room": + { + Room r = GetRoom(id); + if(r == null) + { + World.Instance.SendMessage("@wNo such room (@R" + id + "@w).", i.ClientMask); + return true; + } + + IRooms.Remove(r.Entry); + r.Area.rooms.Remove(r); + OnDeleted(r); + World.Instance.SendMessage("@wDeleted room '@G" + r.Name + "@w'.", i.ClientMask); + } break; + } + + return true; + } + + private bool CommandFind(InputData i) + { + // @"^(room|area)(\s+exact)?(\s+case)?\s+(.+)" + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: map find room [exact] [case] ", i.ClientMask); + World.Instance.SendMessage(" @wmap find area [exact] [case] ", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[1].Value.ToLower() == "room") + { + string Str = i.Arguments.Groups[4].Value; + if(i.Arguments.Groups[3].Length == 0) + Str = Str.ToLower(); + List Found = new List(); + foreach(KeyValuePair x in IRooms) + { + string Name = x.Value.Name; + if(string.IsNullOrEmpty(Name)) + continue; + if(i.Arguments.Groups[3].Length == 0) + Name = Name.ToLower(); + + if(i.Arguments.Groups[2].Length != 0) + { + if(Name == Str) + Found.Add(x.Value); + } + else if(Name.Contains(Str)) + Found.Add(x.Value); + } + + if(Found.Count == 0) + World.Instance.SendMessage("@wFound nothing!", i.ClientMask); + else + { + World.Instance.SendMessage("@WEntry Name Area", i.ClientMask); + World.Instance.SendMessage("@G====== ================================ ===============================", i.ClientMask); + foreach(Room f in Found) + { + World.Instance.SendMessage("@Y" + string.Format("{0,6}", f.Entry) + " @G" + Utility.FormatColoredString(f.Name, -"================================".Length) + " " + (!string.IsNullOrEmpty(f.Area.Name) ? ("@M" + f.Area.Name) : ("@w" + f.Area.Keyword)), i.ClientMask); + } + World.Instance.SendMessage("@wFound @C" + Found.Count + " @wroom" + (Found.Count != 1 ? "s" : "") + ".", i.ClientMask); + } + } + else + { + string Str = i.Arguments.Groups[4].Value; + if(i.Arguments.Groups[3].Length == 0) + Str = Str.ToLower(); + List Found = new List(); + foreach(KeyValuePair x in IAreas) + { + string Name = x.Value.Name; + if(string.IsNullOrEmpty(Name)) + continue; + if(i.Arguments.Groups[3].Length == 0) + Name = Name.ToLower(); + + if(i.Arguments.Groups[2].Length != 0) + { + if(Name == Str) + Found.Add(x.Value); + } + else if(Name.Contains(Str)) + Found.Add(x.Value); + } + + if(Found.Count == 0) + World.Instance.SendMessage("@wFound nothing!", i.ClientMask); + else + { + World.Instance.SendMessage("@WEntry Keyword Name", i.ClientMask); + World.Instance.SendMessage("@G====== ========== ========================================", i.ClientMask); + foreach(Area f in Found) + { + World.Instance.SendMessage("@Y" + string.Format("{0,6}", f.Entry) + " @w" + Utility.FormatColoredString(f.Keyword, -10) + " @M" + f.Name, i.ClientMask); + } + World.Instance.SendMessage("@wFound @C" + Found.Count + " @warea" + (Found.Count != 1 ? "s" : "") + ".", i.ClientMask); + } + } + return true; + } + + private bool CommandMapper(InputData i) + { + World.Instance.SendMessage("@wAvailable mapper commands:", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map all") + " @w- Shows speedwalk that runs through all the rooms (IDs) you entered starting from your current location.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map count") + " @w- Shows how many rooms you have mapped.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map createexit") + " @w- Create a new exit.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map createportal") + " @w- Create a new portal.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map delete") + " @w- Delete a room, area or an exit.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map exits") + " @w- Show exits information or edit them.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map find") + " @w- Find rooms or areas in mapper.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map goto") + " @w- Goto a room or an area.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map import") + " @w- Import data file converted from mush mapper.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map portals") + " @w- List all portals.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map roominfo") + " @w- Show more information about a room or edit it.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map save") + " @w- Save the mapper database now. Enter argument to save to another file.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map unmapped") + " @w- Find unmapped rooms.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map unreconed") + " @w- Find unmapped or unreconed rooms.", i.ClientMask); + return true; + } + + private bool CommandGoto(InputData i) + { + if(!i.Arguments.Success || i.Arguments.Groups[1].Value.Trim().Length == 0) + { + World.Instance.SendMessage("@wGo to the closest room entered.", i.ClientMask); + World.Instance.SendMessage("@wSyntax: map goto [room id] [room id]", i.ClientMask); + World.Instance.SendMessage(" @wmap goto ", i.ClientMask); + World.Instance.SendMessage(" @wmap goto ", i.ClientMask); + return true; + } + + if(CurrentRoomId == uint.MaxValue || GetRoom(CurrentRoomId) == null) + { + World.Instance.SendMessage("@wWe are in an unknown room.", i.ClientMask); + return true; + } + + List roomId = new List(); + Area aTarget = null; + string[] testIds = i.Arguments.Groups[1].Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string x in testIds) + { + uint u; + if(uint.TryParse(x, out u)) + { + if(GetRoom(u) == null) + { + World.Instance.SendMessage("@wNo such room (@R" + u + "@w)!", i.ClientMask); + continue; + } + if(!roomId.Contains(u)) + roomId.Add(u); + } + else + { + roomId = null; + break; + } + } + + if(roomId == null) + aTarget = GetArea(i.Arguments.Groups[1].Value); + + Pathfinder e; + if(roomId != null) + e = new Pathfinder_Entry(roomId.ToArray()); + else if(aTarget != null) + { + if(aTarget.StartRoom != 0) + e = new Pathfinder_Entry(aTarget.StartRoom); + else + e = new Pathfinder_Area(aTarget.Entry); + } + else + e = new Pathfinder_Name(NameTypes.Partial | NameTypes.CaseInsensitive, i.Arguments.Groups[1].Value); + + FillPathFinder(e); + PathfindResult pr = Get(e); + if(!pr.Success) + { + World.Instance.SendMessage("@wCouldn't find a path to any of the rooms entered.", i.ClientMask); + return true; + } + + if(aTarget != null) + World.Instance.SendMessage("@wGoing to area '@C" + pr.Target.Area.Name + "@w'...", i.ClientMask); + else + World.Instance.SendMessage("@wGoing to room '@G" + pr.Target.Name + "@w'...", i.ClientMask); + + Goto(pr); + return true; + } + + private bool CommandCount(InputData i) + { + int thisArea = 0; + int total = 0; + foreach(KeyValuePair x in IRooms) + { + if(x.Value.Area.Keyword == RoomInfoArea) + thisArea++; + if(x.Value.Area.Entry != uint.MaxValue) + total++; + } + World.Instance.SendMessage("@wYou have mapped @G" + thisArea + " @wrooms in this area.", i.ClientMask); + World.Instance.SendMessage("@wYou have @G" + (IAreas.Count - 1) + " @wareas in mapper.", i.ClientMask); + World.Instance.SendMessage("@wYou have mapped @G" + total + " @wrooms of Aardwolf.", i.ClientMask); + return true; + } + + #endregion + + #region Triggers + private bool TriggerNoRecon(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r != null) + { + r.AddCustomFlag("reconed"); + r.AddCustomFlag("norecon"); + } + return false; + } + + private bool TriggerTagsExits(TriggerData t) + { + if(Config.GetInt32("Tags.Exits", 1) == 0) + return false; + + Room r = GetRoom(CurrentRoomId); + + StringBuilder str = new StringBuilder(); + string[] Exits = t.Match.Groups[2].Value.ToLower().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + CheckExit("north", 'n', Exits.Contains("north") || Exits.Contains("(north)"), Exits.Contains("(north)"), RoomInfoExits.ContainsKey('n') ? RoomInfoExits['n'] : 0, str); + CheckExit("east", 'e', Exits.Contains("east") || Exits.Contains("(east)"), Exits.Contains("(east)"), RoomInfoExits.ContainsKey('e') ? RoomInfoExits['e'] : 0, str); + CheckExit("south", 's', Exits.Contains("south") || Exits.Contains("(south)"), Exits.Contains("(south)"), RoomInfoExits.ContainsKey('s') ? RoomInfoExits['s'] : 0, str); + CheckExit("west", 'w', Exits.Contains("west") || Exits.Contains("(west)"), Exits.Contains("(west)"), RoomInfoExits.ContainsKey('w') ? RoomInfoExits['w'] : 0, str); + CheckExit("up", 'u', Exits.Contains("up") || Exits.Contains("(up)"), Exits.Contains("(up)"), RoomInfoExits.ContainsKey('u') ? RoomInfoExits['u'] : 0, str); + CheckExit("down", 'd', Exits.Contains("down") || Exits.Contains("(down)"), Exits.Contains("(down)"), RoomInfoExits.ContainsKey('d') ? RoomInfoExits['d'] : 0, str); + + if(r != null) + { + bool hadCustom = false; + foreach(Exit e in r.exits) + { + if(PathfindResult.IsDirectionCommand(e.Command) != 'x') + continue; + + hadCustom = true; + if(str.Length > 0) + str.Append(" "); + str.Append("@w'@C" + e.Command + "@w'"); + } + + if(!hadCustom && Exits.Contains("custom")) + { + if(str.Length > 0) + str.Append(" "); + str.Append("@Rcustom"); + } + } + + if(str.Length == 0) + str.Append("@Gnone"); + t.Msg.Msg = "@g[Exits: " + str.ToString() + "@g]"; + return false; + } + + private void CheckExit(string Word, char Dir, bool HasExits, bool IsClosed, uint LeadTo, StringBuilder str) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + { + if(!HasExits) + return; + + if(str.Length > 0) + str.Append(" "); + if(LeadTo == uint.MaxValue) + str.Append("@Y"); + else + str.Append("@G"); + str.Append(IsClosed ? ("(" + Word + ")") : Word); + return; + } + + if(LeadTo == uint.MaxValue) + { + if(str.Length > 0) + str.Append(" "); + str.Append("@Y"); + str.Append(IsClosed ? ("(" + Word + ")") : Word); + return; + } + + Exit e = r.GetExit(Dir); + if(e == null) + return; + + if(!HasExits) + { + if(e.HasFlag("hidden")) + { + if(str.Length > 0) + str.Append(" "); + str.Append("@y(" + Word + ")"); + return; + } + if(str.Length > 0) + str.Append(" "); + str.Append("@D(" + Word + ")"); + return; + } + + if(str.Length > 0) + str.Append(" "); + if(e.To.Area.Entry == uint.MaxValue) + str.Append("@R"); + else if(!e.To.HasCustomFlag("reconed") && Config.GetInt32("Tags.Exits.Recon", 0) != 0) + str.Append("@r"); + else + str.Append("@G"); + + if(IsClosed) + str.Append("(" + Word + ")"); + else + str.Append(Word); + } + + private bool TriggerNoRecall(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r != null) + r.AddFlag("norecall"); + return false; + } + + private bool TriggerPrison(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r != null) + r.AddFlag("prison"); + return false; + } + + private bool TriggerReconArea(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + return false; + string msg = Colors.RemoveColors(t.Match.Groups[1].Value, false); + string name = msg.Substring(0, msg.LastIndexOf('(')).Trim(); + msg = msg.Substring(msg.LastIndexOf('(') + 1); + + r.Area.Name = name; + try + { + int min, max; + if(int.TryParse(msg.Substring(0, msg.IndexOf(' ')), out min)) + r.Area.MinLevel = min; + msg = msg.Substring(msg.IndexOf("to ") + 3); + if(int.TryParse(msg.Substring(0, msg.IndexOf(' ')), out max)) + r.Area.MaxLevel = max; + } + catch + { + } + r.AddCustomFlag("reconed"); + return false; + } + + private bool TriggerReconSector(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + return false; + + r.Sector = Colors.RemoveColors(t.Match.Groups[1].Value, false); + return false; + } + + private bool TriggerReconHealRate(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + return false; + + int rate; + if(int.TryParse(t.Match.Groups[1].Value, out rate)) + r.HealRate = rate; + return false; + } + + private bool TriggerReconManaRate(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + return false; + + int rate; + if(int.TryParse(t.Match.Groups[1].Value, out rate)) + r.ManaRate = rate; + return false; + } + + private bool TriggerReconFlags(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + return false; + + string[] fl = Colors.RemoveColors(t.Match.Groups[1].Value, false).Trim().ToLower().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if(r.IFlags != null) + r.IFlags.Clear(); + foreach(string x in fl) + r.AddFlag(x.Trim()); + return false; + } + + private bool TriggerAreasStart(TriggerData t) + { + ListenArea = true; + return false; + } + + private bool TriggerAreasEnd(TriggerData t) + { + ListenArea = false; + return false; + } + + private bool TriggerAreasEntry(TriggerData t) + { + if(!ListenArea) + return false; + + int minLevel, maxLevel, levelLock = 0; + + if(!int.TryParse(t.Match.Groups[1].Value, out minLevel) || + !int.TryParse(t.Match.Groups[2].Value, out maxLevel)) + return false; + + if(t.Match.Groups[3].Length != 0) + { + if(!int.TryParse(t.Match.Groups[3].Value, out levelLock)) + levelLock = 0; + } + + string keyWord = t.Match.Groups[4].Value; + string areaName = t.Match.Groups[5].Value.Trim(); + + Area a = GetArea(keyWord); + if(a == null) + { + a = new Area(++_guidArea); + IAreas[a.Entry] = a; + } + + a.Keyword = keyWord; + a.LevelLock = levelLock; + a.MaxLevel = maxLevel; + a.MinLevel = minLevel; + if(string.IsNullOrEmpty(a.Name) || a.Name.Length <= areaName.Length || !a.Name.StartsWith(areaName)) + a.Name = areaName; + return false; + } + + private bool TriggerWhereLevel(TriggerData t) + { + int minLevel, maxLevel; + if(!int.TryParse(t.Match.Groups[1].Value, out minLevel) || + !int.TryParse(t.Match.Groups[2].Value, out maxLevel)) + return false; + + Room cur = GetRoom(CurrentRoomId); + if(cur != null) + { + cur.Area.MinLevel = minLevel; + cur.Area.MaxLevel = maxLevel; + } + return false; + } + + private bool TriggerWhereName(TriggerData t) + { + string aName = Colors.RemoveColors(t.Match.Groups[1].Value, false).Trim(); + Room cur = GetRoom(CurrentRoomId); + if(cur != null) + cur.Area.Name = aName; + return false; + } + + private bool TriggerJoinedGQ(TriggerData t) + { + HasGQ = true; + return false; + } + + private bool TriggerLeftGQ(TriggerData t) + { + HasGQ = false; + return false; + } + + private bool TriggerCharStatusLevel(TriggerData t) + { + int i; + if(int.TryParse(t.Match.Groups[1].Value, out i)) + Level = i; + return false; + } + + private bool TriggerCharBaseTier(TriggerData t) + { + int i; + if(int.TryParse(t.Match.Groups[1].Value, out i)) + Tier = i; + return false; + } + + private bool TriggerCharBaseRemorts(TriggerData t) + { + int i; + if(int.TryParse(t.Match.Groups[1].Value, out i)) + Remorts = i; + return false; + } + + private bool TriggerRoomInfoNum(TriggerData t) + { + RoomInfoPrevious = RoomInfoEntry; + RoomInfoListen = true; + RoomInfoExits.Clear(); + uint i; + if(!uint.TryParse(t.Match.Groups[1].Value, out i)) + { + RoomInfoEntry = uint.MaxValue; + return false; + } + + RoomInfoEntry = i; + return false; + } + + private bool TriggerRoomInfoName(TriggerData t) + { + RoomInfoName = Colors.RemoveColors(t.Match.Groups[1].Value, true).Trim(); + return false; + } + + private bool TriggerRoomInfoArea(TriggerData t) + { + RoomInfoArea = t.Match.Groups[1].Value.Trim(); + return false; + } + + private bool TriggerRoomInfoExits(TriggerData t) + { + uint exitId; + RoomInfoExits[t.Match.Groups[1].Value.ToLower()[0]] = !uint.TryParse(t.Match.Groups[2].Value, out exitId) + ? uint.MaxValue + : exitId; + return false; + } + + private bool TriggerRoomInfoFinish(TriggerData t) + { + if(!RoomInfoListen) + return false; + + RoomInfoListen = false; + UpdateRoom(); + return false; + } + + #endregion + + #region Data + internal Dictionary IAreas = new Dictionary(); + internal Dictionary IRooms = new Dictionary(); + internal Dictionary IExits = new Dictionary(); + internal Dictionary IPortals = new Dictionary(); + + /// + /// Collection of all areas. + /// + public IEnumerable Areas + { + get + { + return IAreas.Values; + } + } + + /// + /// Collection of all rooms. + /// + public IEnumerable Rooms + { + get + { + return IRooms.Values; + } + } + + /// + /// Collection of all exits. + /// + public IEnumerable Exits + { + get + { + return IExits.Values; + } + } + + /// + /// Collection of all portals. + /// + public IEnumerable Portals + { + get + { + return IPortals.Values; + } + } + + private uint _guidArea = 0; + private uint _guidExit = 0; + + private void UpdateRoom() + { + CurrentRoomId = RoomInfoEntry; + + if(RoomInfoEntry == uint.MaxValue) + { + if(CurrentRoomId != RoomInfoPrevious) + { + Room prev = GetRoom(RoomInfoPrevious); + if(prev != null) + { + GetScript(prev).OnLeaveRoom(prev, null); + GetScript(prev).OnLeaveArea(prev.Area, null); + } + } + return; + } + + Room r = null; + Area a = null; + if(!IRooms.ContainsKey(CurrentRoomId)) + { + r = new Room(CurrentRoomId); + IRooms[r.Entry] = r; + } + else + r = IRooms[CurrentRoomId]; + + a = GetArea(RoomInfoArea); + if(a == null) + { + a = new Area(++_guidArea); + IAreas[a.Entry] = a; + a.Keyword = RoomInfoArea; + } + + if(r.Area != a) + r.Area = a; + + r.Name = RoomInfoName; + bool u = false; + foreach(KeyValuePair e in RoomInfoExits) + { + UpdateRoom(e.Value); + if(e.Value == uint.MaxValue) + continue; + Exit prev = r.GetExit(e.Key); + if(prev != null && prev.ToRoom == e.Value) + continue; + if(prev != null) + r.exits.Remove(prev); + Exit newExit = new Exit(++_guidExit); + newExit.To = GetRoom(e.Value); + newExit.Command = e.Key.ToString(); + newExit.From = r; + r.exits.Add(newExit); + IExits[newExit.Entry] = newExit; + u = true; + } + + if(u) + r.UpdateExits(); + + if(CurrentRoomId != RoomInfoPrevious) + { + Room prev = GetRoom(RoomInfoPrevious); + if(prev != null) + { + GetScript(prev).OnLeaveRoom(prev, r); + GetScript(prev).OnLeaveArea(prev.Area, r.Area); + } + GetScript(r).OnEnterArea(r.Area, prev != null ? prev.Area : null); + GetScript(r).OnEnterRoom(r, prev); + } + } + + private void UpdateRoom(uint Entry) + { + if(Entry == uint.MaxValue) + return; + + Room r = GetRoom(Entry); + if(r == null) + { + r = new Room(Entry); + IRooms[r.Entry] = r; + r.Area = GetArea(uint.MaxValue); + } + } + + internal uint CurrentRoomId; + private uint RoomInfoPrevious = uint.MaxValue; + private uint RoomInfoEntry = uint.MaxValue; + private string RoomInfoName; + private string RoomInfoArea; + private bool RoomInfoListen; + private Dictionary RoomInfoExits = new Dictionary(); + + /// + /// Get room by ID. + /// + /// ID of the room to get. + /// + public Room GetRoom(uint Entry) + { + return IRooms.ContainsKey(Entry) ? IRooms[Entry] : null; + } + + /// + /// Get exit by ID. + /// + /// ID of the exit to get. + /// + public Exit GetExit(uint Entry) + { + return IExits.ContainsKey(Entry) ? IExits[Entry] : null; + } + + /// + /// Get area by ID. + /// + /// ID of the area to get. + /// + public Area GetArea(uint Entry) + { + return IAreas.ContainsKey(Entry) ? IAreas[Entry] : null; + } + + /// + /// Get area by keyword. + /// + /// Keyword of area to get. + /// + public Area GetArea(string Keyword) + { + foreach(KeyValuePair x in IAreas) + { + if(x.Value.Keyword == Keyword) + return x.Value; + } + return null; + } + #endregion + + public override void Shutdown() + { + base.Shutdown(); + + Save(DBFileName); + } + + /// + /// Use mapper to go to the end of pathresult. + /// + /// + public void Goto(PathfindResult pr) + { + string sw = PathfindResult.Speedwalk(pr.Path); + if(string.IsNullOrEmpty(sw)) + sw = ""; + + if(Config.GetInt32("Speedwalk.Echo", 0) != 0) + { + World.Instance.SendMessage("@w{mapperpath}" + sw, Config.GetUInt64("Speedwalk.Echo.AuthMask", ulong.MaxValue)); + } + else if(!string.IsNullOrEmpty(sw)) + { + string[] swp = sw.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string x in swp) + World.Instance.Execute(x, true); + } + else + World.Instance.SendMessage("@wTrying to execute an empty path. Using goto when you are already at target?"); + } + + #region Saving + private void Load() + { + StreamReader f; + try + { + f = new StreamReader(DBFileName); + } + catch + { + // No database exists or we aren't allowed to read it. Make a new database. + return; + } + + Area[] data; + try + { + DataContractSerializer x = new DataContractSerializer(typeof(Area[])); + data = x.ReadObject(f.BaseStream) as Area[]; + } + catch + { + f.Close(); + Log.Error("Failed loading mapper database! File corrupted?"); + return; + } + + f.Close(); + + if(data == null) + return; + + foreach(Area a in data) + { + if(a == null) + continue; + + IAreas[a.Entry] = a; + if(a.Entry > _guidArea && a.Entry != uint.MaxValue) + _guidArea = a.Entry; + foreach(Room r in a.rooms) + { + if(r == null) + continue; + r.Area = a; + IRooms[r.Entry] = r; + foreach(Exit e in r.exits) + { + if(e == null) + continue; + e.From = r; + IExits[e.Entry] = e; + if(e.Entry > _guidExit) + _guidExit = e.Entry; + } + r.UpdateExits(); + } + foreach(Exit p in a.Portals) + { + p.To = GetRoom(p.ToRoom); + IPortals[p.Entry] = p; + if(p.Entry > _guidExit) + _guidExit = p.Entry; + } + } + + foreach(KeyValuePair e in IExits) + { + if(IPortals.ContainsKey(e.Key)) + { + IPortals[e.Key].To.Area.Portals.Remove(IPortals[e.Key]); + IPortals.Remove(e.Key); + } + } + + List toDelete = new List(); + foreach(KeyValuePair e in IExits) + { + e.Value.To = IRooms.ContainsKey(e.Value.ToRoom) ? IRooms[e.Value.ToRoom] : null; + if(e.Value.To == null) + toDelete.Add(e.Value); + } + + foreach(Exit e in toDelete) + { + IExits.Remove(e.Entry); + e.From.exits.Remove(e); + } + + // Successfully loaded a database. Now make a backup because we have a working copy at the moment. + File.Delete(DBFileBackup); + File.Copy(DBFileName, DBFileBackup); + } + + private void Save(string fileName) + { + if(IAreas.Count == 0) + return; + StreamWriter f = new StreamWriter(fileName, false); + + try + { + DataContractSerializer x = new DataContractSerializer(typeof(Area[])); + x.WriteObject(f.BaseStream, IAreas.Values.ToArray()); + } + catch(Exception e) + { + f.Close(); + throw e; + } + + f.Close(); + if(Config.GetInt32("AutoSave", 0) != 0) + WhenSave = World.Instance.MSTime + Config.GetInt32("AutoSave", 0) * 1000; + } + #endregion + + public override void Update(long msTime) + { + base.Update(msTime); + + if(WhenSave == 0 && Config.GetInt32("AutoSave", 0) != 0) + WhenSave = Config.GetInt32("AutoSave", 0) * 1000 + msTime; + else if(WhenSave > 0 && WhenSave <= msTime) + Save(DBFileName); + } + } + + public class MapperConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Tags.Exits", 1, "Display extra information about exits using {exits} tag. You need to have \"tags exits on\"."); + CreateSetting("Tags.Exits.Recon", 0, "Display exits that lead to rooms that haven't been reconed yet in dark red."); + CreateSetting("Speedwalk.Echo", 0, "Echo speedwalk to clients instead of executing the command when using goto. This is useful if you like your MUD client to process it instead of mapper just sending to MUD, for example portal alias etc."); + CreateSetting("Speedwalk.Echo.AuthMask", ulong.MaxValue, "Security mask of who to echo the commands if you set Echo to 1."); + CreateSetting("AutoSave", 0, "Save mapper database every X seconds. For example enter 600 to save mapper database every 10 minutes. Enter 0 to disable this feature. The map is also saved on shutdown of program. You can also type \"map save\" to manually save the database."); + } + } +} diff --git a/Mapper/.svn/text-base/Mapper.csproj.svn-base b/Mapper/.svn/text-base/Mapper.csproj.svn-base new file mode 100644 index 0000000..fd66328 --- /dev/null +++ b/Mapper/.svn/text-base/Mapper.csproj.svn-base @@ -0,0 +1,77 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D} + Library + Properties + Mapper + Mapper + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.0 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Mapper/.svn/text-base/Pathfind.cs.svn-base b/Mapper/.svn/text-base/Pathfind.cs.svn-base new file mode 100644 index 0000000..1a81593 --- /dev/null +++ b/Mapper/.svn/text-base/Pathfind.cs.svn-base @@ -0,0 +1,451 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Mapper.Scripting; + +namespace Mapper +{ + public partial class Mapper + { + private List[] _pGrid = new List[4096]; + private uint _pGridMax = 0; + private List[] _pExits = new[] { new List(), new List() }; + + private void Path_Init() + { + for(int i = 0; i < _pGrid.Length; i++) + _pGrid[i] = new List(512); + } + + private bool CanUsePortal(Exit p, Pathfinder x) + { + if(!p.HasFlag("recallmechanic")) + { + if(!x.CanUsePortals) + return false; + } + else + { + if(!x.CanUseRecalls) + return false; + } + + if(p.MinLevel > x.CharacterLevel + x.CharacterTier * 10 || + p.MaxLevel < x.CharacterLevel) + return false; + + if(p.HasFlag("nogq") && x.IsGlobalQuest) + return false; + + if(x.SkipPortals != null && x.SkipPortals.Contains(p)) + return false; + + return true; + } + + internal static AreaScript GetScript(Room r) + { + return AreaScriptMgr.GetScript(r.Area.Entry); + } + + public PathfindResult Get(Pathfinder p) + { + if(p.StartRooms != null) + p.StartRooms = p.StartRooms.Where(val => val != null).ToArray(); + if(p.StartRooms == null || p.StartRooms.Length == 0) + return new PathfindResult() { Success = false }; + + for(int i = 0; i < _pGridMax; i++) + _pGrid[i].Clear(); + _pExits[0].Clear(); + _pExits[1].Clear(); + _pGridMax = 0; + foreach(KeyValuePair x in IRooms) + { + x.Value.Mapper_OpenBy = null; + x.Value.Mapper_OpenCost = 0; + } + + p.OnStartedPathfind(); + + if(p.OverridePortals == null) + { + foreach(KeyValuePair x in IPortals) + { + if(!x.Value.HasFlag("disabled") && CanUsePortal(x.Value, p)) + _pExits[!x.Value.HasFlag("recallmechanic") ? 0 : 1].Add(x.Value); + } + } + else + { + foreach(Exit x in p.OverridePortals) + { + if(!x.HasFlag("disabled") && CanUsePortal(x, p)) + _pExits[!x.HasFlag("recallmechanic") ? 0 : 1].Add(x); + } + } + + Room Finish = null; + if(p.StartRooms != null) + { + foreach(Room r in p.StartRooms) + { + r.Mapper_OpenCost = uint.MaxValue; + if(OpenRoom(r, null, 0, p)) + { + Finish = r; + break; + } + } + } + + int itr1 = 0, itr2 = 0; + if(Finish == null) + { + while(itr1 < _pGrid.Length && itr1 < _pGridMax) + { + if(itr2 >= _pGrid[itr1].Count) + { + itr2 = 0; + itr1++; + continue; + } + + if(OpenRoom(_pGrid[itr1][itr2].To, _pGrid[itr1][itr2], (uint)itr1, p)) + { + Finish = _pGrid[itr1][itr2].To; + break; + } + + itr2++; + } + } + + PathfindResult pr = new PathfindResult(); + pr.Success = Finish != null; + pr.Cost = itr1; + pr.Target = Finish; + while(Finish != null && Finish.Mapper_OpenBy != null) + { + pr.Path.Insert(0, Finish.Mapper_OpenBy); + Finish = Finish.Mapper_OpenBy.From; + } + pr.Start = pr.Path.Count != 0 ? pr.Path[0].From : Finish; + + p.OnEndedPathFind(pr); + return pr; + } + + private bool OpenRoom(Room r, Exit from, uint cost, Pathfinder p) + { + if(r.Mapper_OpenBy != null || (from != null && p.StartRooms.Contains(r)) || r.Area.Entry == uint.MaxValue) + return false; + + r.Mapper_OpenBy = from; + r.Mapper_OpenCost = cost; + + if(p.IsTargetRoom(r)) + return true; + + if(_pExits[0].Count > 0 && !r.HasFlag("prison")) + { + for(int i = _pExits[0].Count - 1; i >= 0; i--) + { + if(_pExits[0][i].To.Mapper_OpenBy != null) + _pExits[0].RemoveAt(i); + else if(GetScript(r).CanUsePortal(_pExits[0][i], r) && p.CanUsePortal(_pExits[0][i], r)) + { + _pExits[0][i].From = r; + AddExit(_pExits[0][i], p, true); + _pExits[0].RemoveAt(i); + } + } + } + if(_pExits[1].Count > 0 && !r.HasFlag("norecall")) + { + for(int i = _pExits[1].Count - 1; i >= 0; i--) + { + if(_pExits[1][i].To.Mapper_OpenBy != null) + _pExits[1].RemoveAt(i); + else if(GetScript(r).CanUsePortal(_pExits[1][i], r) && p.CanUsePortal(_pExits[1][i], r)) + { + _pExits[1][i].From = r; + AddExit(_pExits[1][i], p, true); + _pExits[1].RemoveAt(i); + } + } + } + foreach(Exit e in r.exits) + { + if(e.To.Mapper_OpenBy != null) + continue; + if(e.To.Area.Entry == uint.MaxValue) + continue; + if(e.HasFlag("disabled")) + continue; + if(!GetScript(r).CanUseExit(e)) + continue; + if(e.MinLevel > p.CharacterLevel) + continue; + if(e.MaxLevel < p.CharacterLevel) + continue; + if(!p.CanUseExit(e)) + continue; + AddExit(e, p, false); + } + + return false; + } + + private void AddExit(Exit e, Pathfinder p, bool isPortal) + { + uint cost = e.From.Mapper_OpenCost + GetScript(e.From).GetLeaveRoomCost(e.From, e) + + (!isPortal ? GetScript(e.From).GetExitCost(e) : GetScript(e.From).GetPortalCost(e)) + GetScript(e.To).GetEnterRoomCost(e.To, e) + + p.GetLeaveRoomCost(e.From, e) + + (!isPortal ? p.GetExitCost(e) : p.GetPortalCost(e)) + p.GetEnterRoomCost(e.To, e); + + uint o = p.GetOverrideCost(e, isPortal); + if(o != uint.MaxValue) + cost = e.From.Mapper_OpenCost + o; + + if(cost >= _pGrid.Length) + return; + _pGrid[cost].Add(e); + cost++; + if(cost > _pGridMax) + _pGridMax = cost; + } + } + + public class PathfindResult + { + internal PathfindResult() + { + } + + public List Path = new List(); + public int Cost = 0; + public bool Success = false; + public Room Target; + public Room Start; + + /// + /// Generate a speedwalk from exit list. + /// + /// List of exits to generate a speedwalk from. + /// + public static string Speedwalk(IEnumerable Exits) + { + List sPath = new List(); + + foreach(Exit x in Exits) + { + string Door = Mapper.GetScript(x.From).GetDoorCommand(x); + string Command = Mapper.GetScript(x.From).GetExitCommand(x); + if(!string.IsNullOrEmpty(Door)) + sPath.AddRange(Door.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); + if(Command == null) + break; + sPath.AddRange(Command.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries)); + } + + StringBuilder strSW = new StringBuilder(); + StringBuilder strCur = new StringBuilder(); + foreach(string x in sPath) + { + if(IsDirectionCommand(x) != 'x') + { + strCur.Append(char.ToLower(x[0])); + } + else + { + if(strCur.Length > 0) + { + if(strSW.Length > 0) + strSW.Append(';'); + if(strCur.Length > 1) + strSW.Append("run "); + strSW.Append(CompressPath(strCur.ToString())); + strCur.Remove(0, strCur.Length); + } + + if(strSW.Length > 0) + strSW.Append(';'); + + strSW.Append(x); + } + } + + if(strCur.Length > 0) + { + if(strSW.Length > 0) + strSW.Append(';'); + if(strCur.Length > 1) + strSW.Append("run "); + strSW.Append(CompressPath(strCur.ToString())); + strCur.Remove(0, strCur.Length); + } + + return strSW.ToString(); + } + + /// + /// Check if command is direction command. For example "west" -> 'w'. If it's not a direction + /// then return 'x'. + /// + /// Command to check. + /// + public static char IsDirectionCommand(string cmd) + { + cmd = cmd.ToLower().Trim(); + + if(cmd.Length == 0) + return 'x'; + + if(cmd.Length <= "north".Length && "north".StartsWith(cmd)) + return 'n'; + if(cmd.Length <= "west".Length && "west".StartsWith(cmd)) + return 'w'; + if(cmd.Length <= "south".Length && "south".StartsWith(cmd)) + return 's'; + if(cmd.Length <= "east".Length && "east".StartsWith(cmd)) + return 'e'; + if(cmd.Length <= "down".Length && "down".StartsWith(cmd)) + return 'd'; + if(cmd.Length <= "up".Length && "up".StartsWith(cmd)) + return 'u'; + return 'x'; + } + + /// + /// Compresses "wwssse" into "2w3se". + /// + /// Path to compress. + /// + public static string CompressPath(string path) + { + char dir = 'x'; + int c = 0; + StringBuilder str = new StringBuilder(); + for(int i = 0; i < path.Length; i++) + { + if(path[i] == dir) + { + c++; + continue; + } + + if(dir == 'x') + { + dir = path[i]; + c = 1; + continue; + } + + if(c > 1) + str.Append(c.ToString()); + str.Append(dir.ToString()); + dir = path[i]; + c = 1; + } + + if(dir != 'x') + { + if(c > 1) + str.Append(c.ToString()); + str.Append(dir.ToString()); + } + + return str.ToString(); + } + } + + public class Pathfinder + { + protected Pathfinder() + { + } + + /// + /// Set the character level for this path find. It is used in level lock areas and portals. + /// + public int CharacterLevel = 1; + public int CharacterTier = 0; + public bool IsGlobalQuest = false; + public bool CanUsePortals = true; + public bool CanUseRecalls = true; + public bool IsSingleClassTier0 = false; + public Room[] SkipRooms; + public Exit[] SkipExits; + public Exit[] SkipPortals; + public Room[] StartRooms; + public Exit[] OverridePortals; + + public virtual void CopyFrom(Pathfinder pf) + { + if(pf == null) + return; + + CharacterLevel = pf.CharacterLevel; + CharacterTier = pf.CharacterTier; + IsGlobalQuest = pf.IsGlobalQuest; + CanUsePortals = pf.CanUsePortals; + CanUseRecalls = pf.CanUseRecalls; + IsSingleClassTier0 = pf.IsSingleClassTier0; + SkipRooms = pf.SkipRooms; + SkipExits = pf.SkipExits; + SkipPortals = pf.SkipPortals; + StartRooms = pf.StartRooms; + OverridePortals = pf.OverridePortals; + } + + public virtual uint GetOverrideCost(Exit e, bool isPortal) + { + return uint.MaxValue; + } + + public virtual void OnStartedPathfind() + { + } + + public virtual bool IsTargetRoom(Room r) + { + return false; + } + + public virtual void OnEndedPathFind(PathfindResult pr) + { + } + + public virtual bool CanUseExit(Exit e) + { + return true; + } + + public virtual bool CanUsePortal(Exit p, Room r) + { + return true; + } + + public virtual uint GetLeaveRoomCost(Room r, Exit e) + { + return 0; + } + + public virtual uint GetExitCost(Exit e) + { + return 0; + } + + public virtual uint GetPortalCost(Exit e) + { + return 0; + } + + public virtual uint GetEnterRoomCost(Room r, Exit e) + { + return 0; + } + } +} diff --git a/Mapper/.svn/text-base/Room.cs.svn-base b/Mapper/.svn/text-base/Room.cs.svn-base new file mode 100644 index 0000000..fd98160 --- /dev/null +++ b/Mapper/.svn/text-base/Room.cs.svn-base @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.Serialization; + +namespace Mapper +{ + [DataContract] + public class Room + { + public Room(uint entry) + { + Entry = entry; + } + + [DataMember] + public readonly uint Entry; + + [DataMember] + public string Name + { + get; + internal set; + } + + [DataMember] + public string Sector + { + get; + internal set; + } + + [DataMember] + internal List IFlags = null; + + public IEnumerable Flags + { + get + { + return IFlags; + } + } + + /// + /// Check if room has a flag. These are actual flags that you get from recon and not custom flags. + /// + /// Flag to check for. + /// + public bool HasFlag(string flag) + { + return IFlags != null && IFlags.Contains(flag.ToLower().Trim()); + } + + /// + /// Add a flag to room. These are actual flags that you get from recon and not custom flags. + /// + /// Flag to add. + public void AddFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(IFlags == null) + IFlags = new List(); + if(!string.IsNullOrEmpty(flag) && !IFlags.Contains(flag)) + IFlags.Add(flag); + } + + /// + /// Remove a flag from room. These are actual flags that you get from recon and not custom flags. + /// + /// Flag to remove. + /// + public bool RemoveFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(IFlags != null && IFlags.Contains(flag)) + { + IFlags.Remove(flag); + return true; + } + return false; + } + + [DataMember] + internal List CFlags = null; + + public IEnumerable CustomFlags + { + get + { + return CFlags; + } + } + + [DataMember] + public uint EntryCost + { + get; + internal set; + } + + [DataMember] + public uint LeaveCost + { + get; + internal set; + } + + /// + /// Check if room has a flag. These are custom flags and not actual flags like norecall which you get from recon. + /// + /// Flag to check for. + /// + public bool HasCustomFlag(string flag) + { + return CFlags != null && CFlags.Contains(flag.ToLower().Trim()); + } + + /// + /// Add a flag to room. These are custom flags and not actual flags like norecall which you get from recon. + /// + /// Flag to add. + public void AddCustomFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(CFlags == null) + CFlags = new List(); + if(!string.IsNullOrEmpty(flag) && !CFlags.Contains(flag)) + CFlags.Add(flag); + } + + /// + /// Remove a flag from room. These are custom flags and not actual flags like norecall which you get from recon. + /// + /// Flag to remove. + /// + public bool RemoveCustomFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(CFlags != null && CFlags.Contains(flag)) + { + CFlags.Remove(flag); + return true; + } + return false; + } + + [DataMember] + public int HealRate + { + get; + internal set; + } + + [DataMember] + public int ManaRate + { + get; + internal set; + } + + [DataMember] + internal List exits = new List(); + + internal uint Mapper_OpenCost = 0; + internal Exit Mapper_OpenBy = null; + + public Exit[] Exits + { + get + { + return exits.ToArray(); + } + } + + private Area _area; + + public Area Area + { + get + { + return _area; + } + internal set + { + if(_area != null) + _area.rooms.Remove(this); + _area = value; + if(!_area.rooms.Contains(this)) + _area.rooms.Add(this); + } + } + + public Exit GetExit(char Direction) + { + foreach(Exit e in exits) + { + if(!string.IsNullOrEmpty(e.Command) && e.Command.Length == 1 && e.Command[0] == Direction) + return e; + } + + return null; + } + + public Exit GetExit(string Command) + { + foreach(Exit e in exits) + { + if(!string.IsNullOrEmpty(e.Command) && e.Command == Command) + return e; + } + + return null; + } + + public Exit GetExit(uint Entry) + { + foreach(Exit e in exits) + { + if(e.Entry == Entry) + return e; + } + + return null; + } + + internal void UpdateExits() + { + SortedDictionary> x = new SortedDictionary>(); + foreach(Exit e in exits) + { + string cmd; + if(string.IsNullOrEmpty(e.Command) || string.IsNullOrEmpty(cmd = e.Command.ToLower().Trim())) + continue; + + e.Command = cmd; + if(!x.ContainsKey(e.Command)) + x[e.Command] = new List(); + x[e.Command].Add(e); + } + + exits.Clear(); + + foreach(KeyValuePair> i in x) + { + foreach(Exit j in i.Value) + exits.Add(j); + } + } + + public override string ToString() + { + return "Room '" + (Name ?? "NULL") + "' [" + Entry + "]"; + } + } +} diff --git a/Mapper/.svn/text-base/desktop.ini b/Mapper/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/.svn/tmp/desktop.ini b/Mapper/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/.svn/tmp/prop-base/desktop.ini b/Mapper/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/.svn/tmp/props/desktop.ini b/Mapper/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/.svn/tmp/text-base/desktop.ini b/Mapper/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Area.cs b/Mapper/Area.cs new file mode 100644 index 0000000..806ed53 --- /dev/null +++ b/Mapper/Area.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; + +namespace Mapper +{ + [DataContract] + public class Area + { + public Area(uint entry) + { + Entry = entry; + } + + [DataMember] + public readonly uint Entry; + + [DataMember] + public string Name + { + get; + internal set; + } + + [DataMember] + public string Keyword + { + get; + internal set; + } + + [DataMember] + public int MinLevel + { + get; + internal set; + } + + [DataMember] + public int MaxLevel + { + get; + internal set; + } + + [DataMember] + public int LevelLock + { + get; + internal set; + } + + [DataMember] + public uint StartRoom + { + get; + internal set; + } + + [DataMember] + internal List rooms = new List(); + + [DataMember] + internal List Portals = new List(); + + public Room[] Rooms + { + get + { + return rooms.ToArray(); + } + } + + [DataMember] + public int ExplorableRooms + { + get; + internal set; + } + + /// + /// Get all rooms in area with this name (exact, case sensitive). + /// + /// Name of room. + /// + public List GetRooms(string Name) + { + List r = new List(); + foreach(Room x in rooms) + { + if(x.Name == Name) + r.Add(x); + } + + return r; + } + + [DataMember] + internal List Flags = null; + + /// + /// Check if area has a flag. + /// + /// Flag to check for. + /// + public bool HasFlag(string flag) + { + return Flags != null && Flags.Contains(flag.ToLower().Trim()); + } + + /// + /// Add a flag to area. + /// + /// Flag to add. + public void AddFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(Flags == null) + Flags = new List(); + if(!string.IsNullOrEmpty(flag) && !Flags.Contains(flag)) + Flags.Add(flag); + } + + /// + /// Remove a flag from area. + /// + /// Flag to remove. + /// + public bool RemoveFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(Flags != null && Flags.Contains(flag)) + { + Flags.Remove(flag); + return true; + } + return false; + } + + public override string ToString() + { + return "Area '" + (!string.IsNullOrEmpty(Name) ? Name : "NULL") + "' [" + Entry + "]"; + } + } +} diff --git a/Mapper/Edit.cs b/Mapper/Edit.cs new file mode 100644 index 0000000..9f71d08 --- /dev/null +++ b/Mapper/Edit.cs @@ -0,0 +1,497 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore; +using ProxyCore.Input; + +namespace Mapper +{ + public partial class Mapper + { + private bool CommandExit(InputData i) + { + // 1 2 3 4 + // @"^(help)?(room\s+\d+)?(\d+)?(\s+.+)?" + if(i.Arguments.Groups[1].Length != 0) + { + World.Instance.SendMessage("@wSyntax:", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map exit") + " - show exits in current room.", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map exit help") + " - show this message.", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map exit room ") + " - show exits in room.", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map exit [option] [value]") + " - change exit options / show information by exit ID.", i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage("@wAvailable options for exit:", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "addflag") + " @w- Add a flag to exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "removeflag") + " @w- Remove a flag from exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "command") + " @w- Change command that activates exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "doorcommand") + " @w- Change command that we use to open door, for example '@Wopen altar@w'. Use clear to remove this field.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "cost") + " @w- Change cost of using exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "minlevel") + " @w- Minimum level required to use exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "maxlevel") + " @w- Maximum level allowed to use exit.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "delete confirm") + " @w- Delete the exit.", i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage("@wAvailable default flags:", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "recallmechanic") + " @w- This exit uses recall mechanic, can't use in norecall (for portals).", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "door") + " @w- This exit has a door.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "nogq") + " @w- Don't use this exit on a global quest (for regular chaos portals).", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "disabled") + " @w- Don't use this exit ever.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "nopass") + " @w- The door is nopass.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "locked") + " @w- The door is locked.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "pickable") + " @w- Door can be pick locked or bashdoor.", i.ClientMask); + World.Instance.SendMessage("@wNote: there may be custom flags that are implemented by other plugins / scripts that aren't listed here.", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[2].Length != 0 || (i.Arguments.Groups[2].Length == 0 && i.Arguments.Groups[1].Length == 0 && i.Arguments.Groups[3].Length == 0)) + { + uint roomId; + if(i.Arguments.Groups[2].Length != 0) + { + string str = i.Arguments.Groups[2].Value.Substring(4).Trim(); + if(!uint.TryParse(str, out roomId)) + { + World.Instance.SendMessage("@wInvalid room ID given. Type '@Wmap exit help@w' for syntax.", + i.ClientMask); + return true; + } + } + else + { + roomId = CurrentRoomId; + if(roomId == uint.MaxValue) + { + World.Instance.SendMessage("@wYou are in an unknown room.", i.ClientMask); + return true; + } + } + + Room r = GetRoom(roomId); + if(r == null) + { + World.Instance.SendMessage("@wNo such room in database (@R" + roomId + "@w).", i.ClientMask); + return true; + } + + World.Instance.SendMessage("@wExits in '@G" + r.Name + "@w' [@Y" + r.Entry + "@w]:", i.ClientMask); + if(r.exits.Count == 0) + World.Instance.SendMessage("@wNo exits found.", i.ClientMask); + else + { + World.Instance.SendMessage("@WEntry Command To", i.ClientMask); + World.Instance.SendMessage("@G====== ==================== ========================", i.ClientMask); + foreach(Exit e in r.exits) + World.Instance.SendMessage("@Y" + string.Format("{0,-6}", e.Entry) + " @W" + string.Format("{0,-20}", e.Command) + " @w[@Y" + string.Format("{0,6}", e.To.Entry) + "@w] @G" + e.To.Name, i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage("@wType '@Wmap exit @w' for more information about an exit.", i.ClientMask); + World.Instance.SendMessage("@wOr '@Wmap exit help@w' for syntax.", i.ClientMask); + } + return true; + } + + if(i.Arguments.Groups[3].Length != 0) + { + uint exitId; + if(!uint.TryParse(i.Arguments.Groups[3].Value, out exitId)) + { + World.Instance.SendMessage("@wInvalid exit ID given. Type '@Wmap exit help@w' for syntax.", i.ClientMask); + return true; + } + + Exit e = GetExit(exitId); + if(e == null && IPortals.ContainsKey(exitId)) + e = IPortals[exitId]; + if(e == null) + { + World.Instance.SendMessage("@wNo such exit in database (@R" + exitId + "@w).", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[4].Length != 0) + { + string key, val; + string str = i.Arguments.Groups[4].Value.Trim(); + if(str.Contains(' ')) + { + key = str.Substring(0, str.IndexOf(' ')).ToLower(); + val = str.Substring(key.Length).Trim(); + + switch(key) + { + case "addflag": + e.AddFlag(val); + break; + + case "removeflag": + e.RemoveFlag(val); + break; + + case "command": + val = val.ToLower(); + e.Command = PathfindResult.IsDirectionCommand(val) != 'x' ? + PathfindResult.IsDirectionCommand(val).ToString() : + val; + break; + + case "doorcommand": + val = val.ToLower(); + e.DoorCommand = val != "clear" ? val : null; + break; + + case "cost": + { + uint u; + if(uint.TryParse(val, out u)) + e.Cost = u; + } break; + + case "minlevel": + { + int lvl; + if(int.TryParse(val, out lvl)) + e.MinLevel = lvl; + } break; + + case "maxlevel": + { + int lvl; + if(int.TryParse(val, out lvl)) + e.MaxLevel = lvl; + } break; + + case "delete": + { + if(val == "confirm") + { + if(e.HasFlag("portal")) + { + IPortals.Remove(e.Entry); + e.To.Area.Portals.Remove(e); + } + else + { + IExits.Remove(e.Entry); + e.From.exits.Remove(e); + } + World.Instance.SendMessage("@wDeleted exit or portal (@R" + e.Entry + "@w).", + i.ClientMask); + return true; + } + World.Instance.SendMessage("@wEnter '@Wmap exit delete confirm@w' to remove the exit.", i.ClientMask); + } break; + + default: + World.Instance.SendMessage("@wInvalid key value pair entered '@R" + key + " " + val + "@w'.", i.ClientMask); + World.Instance.SendMessage("@wUse '@Wmap exit help@w' to see syntax.", i.ClientMask); + break; + } + } + } + + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + World.Instance.SendMessage("@w| @WEntry @w: @Y" + string.Format("{0,-55}", e.Entry) + "@w|", i.ClientMask); + if(!e.HasFlag("portal")) + World.Instance.SendMessage("@w| @WFrom @w: @w[@Y" + string.Format("{0,6}", e.From.Entry) + "@w] @G" + Utility.FormatColoredString(!string.IsNullOrEmpty(e.From.Name) ? e.From.Name : "Unknown", -46) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WTo @w: @w[@Y" + string.Format("{0,6}", e.To.Entry) + "@w] @G" + Utility.FormatColoredString(!string.IsNullOrEmpty(e.To.Name) ? e.To.Name : "Unknown", -46) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WCommand @w: @c" + string.Format("{0,-55}", e.Command) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WDoor command@w: @c" + string.Format("{0,-55}", !string.IsNullOrEmpty(e.DoorCommand) ? e.DoorCommand : "none") + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WCost @w: @C" + string.Format("{0,-55}", e.Cost) + "@w|", i.ClientMask); + StringBuilder strFlags = new StringBuilder(); + if(e.IFlags != null) + { + foreach(string x in e.IFlags) + { + if(strFlags.Length > 0) + strFlags.Append(", "); + strFlags.Append(x); + } + } + + string[] spl = Utility.WrapColored(strFlags.ToString(), 54, 0); + for(int j = 0; j < spl.Length; j++) + { + if(j == 0) + World.Instance.SendMessage("@w| @WFlags @w: " + string.Format("{0,-55}", spl[j]) + "@w|", i.ClientMask); + else + World.Instance.SendMessage("@w| : " + string.Format("{0,-55}", spl[j]) + "@w|", i.ClientMask); + } + + World.Instance.SendMessage("@w| @WMin level @w: @W" + string.Format("{0,-55}", e.MinLevel) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WMax level @w: @W" + string.Format("{0,-55}", e.MaxLevel) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + if(i.Arguments.Groups[4].Length == 0) + World.Instance.SendMessage("@wSee '@Wmap exit help@w' for information on how to edit this exit.", i.ClientMask); + return true; + } + + World.Instance.SendMessage("0", i.ClientMask); + return true; + } + + private bool CommandPortal(InputData i) + { + if(IPortals.Count == 0) + { + World.Instance.SendMessage("@wYou have no portals set.", i.ClientMask); + return true; + } + + int count = 0; + World.Instance.SendMessage("@WEntry Command To", i.ClientMask); + World.Instance.SendMessage("@G======== ================ =====================================", i.ClientMask); + foreach(KeyValuePair x in IPortals) + { + World.Instance.SendMessage("@w[@Y" + string.Format("{0,6}", x.Key) + "@w] @w" + string.Format("{0,-16}", x.Value.Command) + " @M" + x.Value.To.Area.Name + " @wroom [@Y" + x.Value.To.Entry + "@w]", i.ClientMask); + count++; + } + + World.Instance.SendMessage("@C" + count + " @wportals found.", i.ClientMask); + return true; + } + + private bool CommandCreateExit(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: map createexit [from room ID = current] \"\"", i.ClientMask); + return true; + } + + uint fromId; + uint toId; + if(i.Arguments.Groups[2].Length > 0) + { + if(!uint.TryParse(i.Arguments.Groups[1].Value, out fromId) || + !uint.TryParse(i.Arguments.Groups[2].Value.Trim(), out toId)) + { + World.Instance.SendMessage("@wSyntax: map createexit [from room ID = current] \"\"", i.ClientMask); + return true; + } + } + else + { + fromId = CurrentRoomId; + if(!uint.TryParse(i.Arguments.Groups[1].Value, out toId)) + { + World.Instance.SendMessage("@wSyntax: map createexit [from room ID = current] \"\"", i.ClientMask); + return true; + } + } + + Room from = GetRoom(fromId); + Room to = GetRoom(toId); + if(from == null || to == null) + { + World.Instance.SendMessage("@wNo such room exists in mapper database.", i.ClientMask); + return true; + } + + char dir = PathfindResult.IsDirectionCommand(i.Arguments.Groups[3].Value); + Exit e = new Exit(++_guidExit); + e.Command = dir != 'x' ? char.ToLower(dir).ToString() : i.Arguments.Groups[3].Value.ToLower(); + e.From = from; + e.To = to; + e.From.exits.Add(e); + e.From.UpdateExits(); + IExits[e.Entry] = e; + World.Instance.SendMessage("@wCreated a new exit (@R" + e.Entry + "@w).", i.ClientMask); + World.Instance.SendMessage("@wType '@Wmap exit " + e.Entry + "@w' for more information or to edit.", i.ClientMask); + return true; + } + + private bool CommandCreatePortal(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: map createportal \"\"", i.ClientMask); + return true; + } + + uint toId; + if(!uint.TryParse(i.Arguments.Groups[1].Value, out toId)) + { + World.Instance.SendMessage("@wSyntax: map createportal \"\"", i.ClientMask); + return true; + } + + Room to = GetRoom(toId); + if(to == null) + { + World.Instance.SendMessage("@wNo such room exists in mapper database.", i.ClientMask); + return true; + } + + Exit e = new Exit(++_guidExit); + e.Command = i.Arguments.Groups[2].Value.ToLower(); + e.To = to; + e.Cost = 5; + if(e.IFlags == null) + e.IFlags = new List(); + e.IFlags.Add("portal"); + IPortals[e.Entry] = e; + to.Area.Portals.Add(e); + World.Instance.SendMessage("@wCreated a new portal (@R" + e.Entry + "@w).", i.ClientMask); + World.Instance.SendMessage("@wType '@Wmap exit " + e.Entry + "@w' for more information or to edit.", i.ClientMask); + return true; + } + + private bool CommandRoomInfo(InputData i) + { + // 1 2 3 + // @"(help)?(\d+)?(\s+.+)?" + if(i.Arguments.Success && i.Arguments.Groups[1].Length != 0) + { + World.Instance.SendMessage("@wSyntax:", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map room") + " - show info about current room.", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map room help") + " - show this message.", i.ClientMask); + World.Instance.SendMessage(string.Format("{0,-20}", "map room [option] [value]") + " - show info about room by ID or change it.", i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage("@wAvailable options for room:", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "addflag") + " @w- Add a flag to room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "removeflag") + " @w- Remove a flag from room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "addcflag") + " @w- Add a custom flag to room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "removecflag") + " @w- Remove a custom flag from room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "name") + " @w- Change name of room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "entrycost") + " @w- Change cost of entering room.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "leavecost") + " @w- Change cost of leaving room.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "healrate") + " @w- Change heal rate.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "manarate") + " @w- Change mana rate.", i.ClientMask); + //World.Instance.SendMessage("@W" + string.Format("{0,-15}", "sector") + " @w- Change sector.", i.ClientMask); + return true; + } + + uint roomId; + if(i.Arguments.Success && i.Arguments.Groups[2].Length != 0) + { + if(!uint.TryParse(i.Arguments.Groups[2].Value, out roomId)) + { + World.Instance.SendMessage("@wInvalid room ID given (@R" + i.Arguments.Groups[2].Value + "@w).", i.ClientMask); + World.Instance.SendMessage("@wUse '@Wmap roominfo help@w' to see syntax.", i.ClientMask); + return true; + } + } + else + { + roomId = CurrentRoomId; + if(roomId == uint.MaxValue) + { + World.Instance.SendMessage("@wYou are in an invalid room.", i.ClientMask); + World.Instance.SendMessage("@wUse '@Wmap roominfo help@w' to see syntax.", i.ClientMask); + return true; + } + } + + Room r = GetRoom(roomId); + if(r == null) + { + World.Instance.SendMessage("@wNo such room in database (@R" + roomId + "@w).", i.ClientMask); + return true; + } + + if(i.Arguments.Success && i.Arguments.Groups[2].Length != 0 && i.Arguments.Groups[3].Length != 0) + { + string key, val; + string str = i.Arguments.Groups[3].Value.Trim(); + if(str.Contains(' ')) + { + key = str.Substring(0, str.IndexOf(' ')).ToLower(); + val = str.Substring(key.Length).Trim(); + + switch(key) + { + case "addflag": + r.AddFlag(val); + break; + + case "removeflag": + r.RemoveFlag(val); + break; + + case "addcflag": + r.AddCustomFlag(val); + break; + + case "removecflag": + r.RemoveCustomFlag(val); + break; + + case "name": + r.Name = val; + break; + + case "entrycost": + { + uint u; + if(uint.TryParse(val, out u)) + r.EntryCost = u; + } break; + + case "leavecost": + { + uint u; + if(uint.TryParse(val, out u)) + r.LeaveCost = u; + } break; + } + } + } + + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + World.Instance.SendMessage("@w| @WEntry @w: @Y" + string.Format("{0,-55}", r.Entry) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WName @w: @G" + Utility.FormatColoredString(!string.IsNullOrEmpty(r.Name) ? r.Name : "Unknown", -55) + "@w|", i.ClientMask); + World.Instance.SendMessage("@w| @WArea @w: @M" + string.Format("{0,-55}", !string.IsNullOrEmpty(r.Area.Name) ? r.Area.Name : "Unknown") + "@w|", i.ClientMask); + { + StringBuilder strFlags = new StringBuilder(); + if(r.IFlags != null) + { + foreach(string x in r.IFlags) + { + if(strFlags.Length > 0) + strFlags.Append(", "); + strFlags.Append(x); + } + } + + string[] spl = Utility.WrapColored(strFlags.ToString(), 54, 0); + for(int j = 0; j < spl.Length; j++) + { + if(j == 0) + World.Instance.SendMessage("@w| @WFlags @w: " + string.Format("{0,-55}", spl[j]) + "@w|", + i.ClientMask); + else + World.Instance.SendMessage("@w| : " + string.Format("{0,-55}", spl[j]) + "@w|", + i.ClientMask); + } + } + + { + StringBuilder strFlags = new StringBuilder(); + if(r.CFlags != null) + { + foreach(string x in r.CFlags) + { + if(strFlags.Length > 0) + strFlags.Append(", "); + strFlags.Append(x); + } + } + + string[] spl = Utility.WrapColored(strFlags.ToString(), 54, 0); + for(int j = 0; j < spl.Length; j++) + { + if(j == 0) + World.Instance.SendMessage("@w| @WCustom flags@w: " + string.Format("{0,-55}", spl[j]) + "@w|", i.ClientMask); + else + World.Instance.SendMessage("@w| : " + string.Format("{0,-55}", spl[j]) + "@w|", i.ClientMask); + } + } + + // Healrate, manarate, sector + + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + if(!i.Arguments.Success || (i.Arguments.Groups[1].Length == 0 && i.Arguments.Groups[2].Length == 0 && i.Arguments.Groups[3].Length == 0)) + World.Instance.SendMessage("@wUse '@Wmap roominfo help@w' to see syntax.", i.ClientMask); + return true; + } + } +} diff --git a/Mapper/Exit.cs b/Mapper/Exit.cs new file mode 100644 index 0000000..e36b754 --- /dev/null +++ b/Mapper/Exit.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; + +namespace Mapper +{ + [DataContract] + public class Exit + { + public Exit(uint entry) + { + Entry = entry; + MinLevel = 0; + MaxLevel = 210; + Cost = 1; + } + + [DataMember] + public readonly uint Entry; + + [DataMember] + public int MinLevel + { + get; + internal set; + } + + [DataMember] + public int MaxLevel + { + get; + internal set; + } + + public Room From + { + get; + internal set; + } + + public Room To + { + get + { + return _to; + } + internal set + { + _to = value; + ToRoom = value != null ? value.Entry : uint.MaxValue; + } + } + + private Room _to; + + [DataMember] + internal uint ToRoom; + + [DataMember] + public string Command + { + get; + internal set; + } + + [DataMember] + public string DoorCommand + { + get; + internal set; + } + + [DataMember] + internal List IFlags = null; + + public IEnumerable Flags + { + get + { + return IFlags; + } + } + + [DataMember] + public uint Cost + { + get; + internal set; + } + + /// + /// Check if exit has a flag. + /// + /// Flag to check for. + /// + public bool HasFlag(string flag) + { + return IFlags != null && IFlags.Contains(flag.ToLower().Trim()); + } + + /// + /// Add a flag to exit. + /// + /// Flag to add. + public void AddFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(flag == "portal") + return; + if(IFlags == null) + IFlags = new List(); + if(!string.IsNullOrEmpty(flag) && !IFlags.Contains(flag)) + IFlags.Add(flag); + } + + /// + /// Remove a flag from exit. + /// + /// Flag to remove. + /// + public bool RemoveFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(flag == "portal") + return false; + if(IFlags != null && IFlags.Contains(flag)) + { + IFlags.Remove(flag); + return true; + } + return false; + } + } +} diff --git a/Mapper/Finders/.svn/all-wcprops b/Mapper/Finders/.svn/all-wcprops new file mode 100644 index 0000000..4208dea --- /dev/null +++ b/Mapper/Finders/.svn/all-wcprops @@ -0,0 +1,35 @@ +K 25 +svn:wc:ra_dav:version-url +V 37 +/svn/!svn/ver/44/trunk/Mapper/Finders +END +Name.cs +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/!svn/ver/2/trunk/Mapper/Finders/Name.cs +END +Entry.cs +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/!svn/ver/2/trunk/Mapper/Finders/Entry.cs +END +VisitAll.cs +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/!svn/ver/44/trunk/Mapper/Finders/VisitAll.cs +END +Area.cs +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/!svn/ver/2/trunk/Mapper/Finders/Area.cs +END +Unmapped.cs +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2/trunk/Mapper/Finders/Unmapped.cs +END diff --git a/Mapper/Finders/.svn/desktop.ini b/Mapper/Finders/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Finders/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Finders/.svn/dir-prop-base b/Mapper/Finders/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/Mapper/Finders/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/Mapper/Finders/.svn/entries b/Mapper/Finders/.svn/entries new file mode 100644 index 0000000..fdb2c4c --- /dev/null +++ b/Mapper/Finders/.svn/entries @@ -0,0 +1,198 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/Mapper/Finders +http://proxymud.googlecode.com/svn + + + +2012-01-29T09:57:36.978045Z +44 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +Unmapped.cs +file + + + + +2016-03-25T22:18:43.048137Z +3184f09287a1efa19af1f9e79499a15a +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1343 + +Name.cs +file + + + + +2016-03-25T22:18:43.048137Z +b0b7b02f4e344ad61cd3ae2b0433ff8e +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +3188 + +Entry.cs +file + + + + +2016-03-25T22:18:43.048137Z +3c5604f9f58dcd4dc52b100067cec01c +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +516 + +VisitAll.cs +file + + + + +2016-03-25T22:18:43.048137Z +212272af6341079b4576c8afb1128661 +2012-01-29T09:57:36.978045Z +44 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +6028 + +Area.cs +file + + + + +2016-03-25T22:18:43.048137Z +6a503a15d8656341f4e3525b25973d51 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +477 + diff --git a/Mapper/Finders/.svn/prop-base/desktop.ini b/Mapper/Finders/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Finders/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Finders/.svn/props/desktop.ini b/Mapper/Finders/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Finders/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Finders/.svn/text-base/Area.cs.svn-base b/Mapper/Finders/.svn/text-base/Area.cs.svn-base new file mode 100644 index 0000000..4570a20 --- /dev/null +++ b/Mapper/Finders/.svn/text-base/Area.cs.svn-base @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Mapper +{ + public class Pathfinder_Area : Pathfinder + { + public Pathfinder_Area(params uint[] AreaID) + : base() + { + AreaId = AreaID; + } + + private readonly uint[] AreaId; + + public override bool IsTargetRoom(Room r) + { + return AreaId.Contains(r.Area.Entry); + } + } +} diff --git a/Mapper/Finders/.svn/text-base/Entry.cs.svn-base b/Mapper/Finders/.svn/text-base/Entry.cs.svn-base new file mode 100644 index 0000000..cb5e0ee --- /dev/null +++ b/Mapper/Finders/.svn/text-base/Entry.cs.svn-base @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Mapper +{ + public class Pathfinder_Entry : Pathfinder + { + public Pathfinder_Entry(params uint[] roomEntries) + : base() + { + TargetRoomEntries = roomEntries; + } + + public readonly uint[] TargetRoomEntries; + + public override bool IsTargetRoom(Room r) + { + return TargetRoomEntries.Contains(r.Entry); + } + } +} diff --git a/Mapper/Finders/.svn/text-base/Name.cs.svn-base b/Mapper/Finders/.svn/text-base/Name.cs.svn-base new file mode 100644 index 0000000..c0d80f3 --- /dev/null +++ b/Mapper/Finders/.svn/text-base/Name.cs.svn-base @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Mapper +{ + public class Pathfinder_Name : Pathfinder + { + public Pathfinder_Name(NameTypes t, params string[] Names) + : base() + { + nt = t; + _names = Names; + if((t & NameTypes.Regex) != NameTypes.None) + { + _regexString = new Regex[Names.Length]; + for(int i = 0; i < Names.Length; i++) + { + if(string.IsNullOrEmpty(Names[i])) + continue; + + try + { + _regexString[i] = new Regex(Names[i], (t & NameTypes.CaseInsensitive) != NameTypes.None ? RegexOptions.IgnoreCase : RegexOptions.None); + } + catch + { + // User entered invalid regex pattern + } + } + } + else if((t & NameTypes.CaseInsensitive) != NameTypes.None) + { + for(int i = 0; i < _names.Length; i++) + _names[i] = _names[i].ToLower(); + } + } + + private readonly Regex[] _regexString; + private readonly string[] _names; + private readonly NameTypes nt; + + public override bool IsTargetRoom(Room r) + { + if(string.IsNullOrEmpty(r.Name)) + return false; + + if((nt & NameTypes.Regex) != NameTypes.None) + { + foreach(Regex x in _regexString) + { + if(x == null) + continue; + + if(x.Match(r.Name).Success) + return true; + } + } + else + { + string rname = r.Name; + if((nt & NameTypes.CaseInsensitive) != NameTypes.None) + rname = rname.ToLower(); + foreach(string x in _names) + { + if((nt & NameTypes.Partial) != NameTypes.None) + { + if(rname.Contains(x)) + return true; + } + else if(rname == x) + return true; + } + } + + return false; + } + } + + [Flags] + public enum NameTypes + { + /// + /// Exact name search. Room name must match what you entered and case sensitive. + /// + None = 0, + + /// + /// Room name must contain the string you entered. This setting is ignored if you set regex option. + /// + Partial = 1, + + /// + /// Room name vs. what you entered is not case sensitive. + /// + CaseInsensitive = 2, + + /// + /// You entered a regex string which must match room name. + /// + Regex = 4, + } +} diff --git a/Mapper/Finders/.svn/text-base/Unmapped.cs.svn-base b/Mapper/Finders/.svn/text-base/Unmapped.cs.svn-base new file mode 100644 index 0000000..26b5ce7 --- /dev/null +++ b/Mapper/Finders/.svn/text-base/Unmapped.cs.svn-base @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Mapper +{ + public class Pathfinder_Unmapped : Pathfinder + { + public Pathfinder_Unmapped(bool unReconed) + : base() + { + UnReconed = unReconed; + } + + private bool UnReconed; + private uint[] AllowedAreas; + + public override void OnStartedPathfind() + { + base.OnStartedPathfind(); + + List a = new List(); + foreach(Room r in StartRooms) + { + if(r.Area.Entry == uint.MaxValue) + continue; + if(!a.Contains(r.Area.Entry)) + a.Add(r.Area.Entry); + } + + AllowedAreas = a.ToArray(); + } + + public override bool IsTargetRoom(Room r) + { + if(!AllowedAreas.Contains(r.Area.Entry)) + return false; + + foreach(Exit e in r.exits) + { + if(e.To.Area.Entry == uint.MaxValue) + return true; + } + + if(UnReconed) + { + if(!r.HasCustomFlag("reconed")) + return true; + } + + return base.IsTargetRoom(r); + } + } +} diff --git a/Mapper/Finders/.svn/text-base/VisitAll.cs.svn-base b/Mapper/Finders/.svn/text-base/VisitAll.cs.svn-base new file mode 100644 index 0000000..b251444 --- /dev/null +++ b/Mapper/Finders/.svn/text-base/VisitAll.cs.svn-base @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Mapper +{ + public class PathFinder_VisitAll : Pathfinder + { + public PathFinder_VisitAll(Mapper mapper, params Room[] roomId) + : base() + { + map = mapper; + foreach(Room u in roomId) + { + if(!AllRooms.Contains(u)) + AllRooms.Add(u); + } + + if(_grid[0] == null) + { + for(int i = 0; i < _grid.Length; i++) + _grid[i] = new List(512); + } + + for(int i = 0; i < _grid.Length; i++) + _grid[i].Clear(); + } + + private readonly Mapper map; + private readonly List AllRooms = new List(); + private readonly Dictionary> Costs = new Dictionary>(); + private static List[] _grid = new List[1024]; + + public override bool IsTargetRoom(Room r) + { + return true; + } + + public override void OnStartedPathfind() + { + base.OnStartedPathfind(); + + foreach(Room x in StartRooms) + AllRooms.Remove(x); + } + + public override void OnEndedPathFind(PathfindResult pr) + { + base.OnEndedPathFind(pr); + + foreach(Room x in StartRooms) + AllRooms.Add(x); + + foreach(Room u in AllRooms) + { + _Internal_PathFinder_VisitAll pf = new _Internal_PathFinder_VisitAll((from val in AllRooms where val != u select val).ToArray()); + pf.CanUsePortals = CanUsePortals; + pf.CanUseRecalls = CanUseRecalls; + pf.CharacterLevel = CharacterLevel; + pf.CharacterTier = CharacterTier; + pf.IsGlobalQuest = IsGlobalQuest; + pf.IsSingleClassTier0 = IsSingleClassTier0; + pf.OverridePortals = OverridePortals; + pf.SkipExits = SkipExits; + pf.SkipPortals = SkipPortals; + pf.SkipRooms = SkipRooms; + pf.StartRooms = new[] { u }; + + PathfindResult p = map.Get(pf); + if(!p.Success) + { + pr.Success = false; + return; + } + + Costs[u.Entry] = new Dictionary(); + foreach(Room x in AllRooms) + { + if(x == u) + continue; + + if(x.Mapper_OpenBy != null) + Costs[u.Entry][x.Entry] = x.Mapper_OpenCost; + else + { + pr.Success = false; + return; + } + } + } + + OpenRoom(new[] { StartRooms[0].Entry }, 0); + + uint[] Finish = null; + int itr1 = 0, itr2 = 0; + while(itr1 < _grid.Length) + { + if(itr2 >= _grid[itr1].Count) + { + itr2 = 0; + _grid[itr1].Clear(); + itr1++; + continue; + } + + if(OpenRoom(_grid[itr1][itr2], (uint)itr1)) + { + Finish = _grid[itr1][itr2]; + break; + } + + itr2++; + } + + if(Finish == null) + pr.Success = false; + else + { + pr.Cost = itr1; + pr.Path.Clear(); + pr.Target = map.GetRoom(Finish[Finish.Length - 1]); + Room u = map.GetRoom(Finish[0]); + int j = 1; + while(u != null && j < Finish.Length) + { + Pathfinder_Entry pf = new Pathfinder_Entry(Finish[j]); + pf.StartRooms = new[] { u }; + pf.CanUsePortals = CanUsePortals; + pf.CanUseRecalls = CanUseRecalls; + pf.CharacterLevel = CharacterLevel; + pf.CharacterTier = CharacterTier; + pf.IsGlobalQuest = IsGlobalQuest; + pf.IsSingleClassTier0 = IsSingleClassTier0; + pf.OverridePortals = OverridePortals; + pf.SkipExits = SkipExits; + pf.SkipPortals = SkipPortals; + pf.SkipRooms = SkipRooms; + + PathfindResult p = map.Get(pf); + if(!p.Success) + { + pr.Success = false; + return; + } + + pr.Path.AddRange(p.Path); + u = map.GetRoom(Finish[j]); + j++; + } + } + } + + private bool OpenRoom(uint[] r, uint cost) + { + if(r.Length == AllRooms.Count) + return true; + + foreach(KeyValuePair x in Costs[r[r.Length - 1]]) + { + if(r.Contains(x.Key)) + continue; + + uint[] z = r.Concat(new[] { x.Key }).ToArray(); + uint c = cost + x.Value; + if(c < _grid.Length) + _grid[(int)c].Add(z); + } + + return false; + } + } + + public class _Internal_PathFinder_VisitAll : Pathfinder + { + public _Internal_PathFinder_VisitAll(params Room[] roomId) + { + AllRooms.AddRange(roomId); + } + + private readonly List AllRooms = new List(); + + public override bool IsTargetRoom(Room r) + { + AllRooms.Remove(r); + return AllRooms.Count == 0; + } + } +} diff --git a/Mapper/Finders/.svn/text-base/desktop.ini b/Mapper/Finders/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Finders/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Finders/.svn/tmp/desktop.ini b/Mapper/Finders/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Finders/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Finders/.svn/tmp/prop-base/desktop.ini b/Mapper/Finders/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Finders/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Finders/.svn/tmp/props/desktop.ini b/Mapper/Finders/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Finders/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Finders/.svn/tmp/text-base/desktop.ini b/Mapper/Finders/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Finders/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Finders/Area.cs b/Mapper/Finders/Area.cs new file mode 100644 index 0000000..4570a20 --- /dev/null +++ b/Mapper/Finders/Area.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Mapper +{ + public class Pathfinder_Area : Pathfinder + { + public Pathfinder_Area(params uint[] AreaID) + : base() + { + AreaId = AreaID; + } + + private readonly uint[] AreaId; + + public override bool IsTargetRoom(Room r) + { + return AreaId.Contains(r.Area.Entry); + } + } +} diff --git a/Mapper/Finders/Entry.cs b/Mapper/Finders/Entry.cs new file mode 100644 index 0000000..cb5e0ee --- /dev/null +++ b/Mapper/Finders/Entry.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Mapper +{ + public class Pathfinder_Entry : Pathfinder + { + public Pathfinder_Entry(params uint[] roomEntries) + : base() + { + TargetRoomEntries = roomEntries; + } + + public readonly uint[] TargetRoomEntries; + + public override bool IsTargetRoom(Room r) + { + return TargetRoomEntries.Contains(r.Entry); + } + } +} diff --git a/Mapper/Finders/Name.cs b/Mapper/Finders/Name.cs new file mode 100644 index 0000000..c0d80f3 --- /dev/null +++ b/Mapper/Finders/Name.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Mapper +{ + public class Pathfinder_Name : Pathfinder + { + public Pathfinder_Name(NameTypes t, params string[] Names) + : base() + { + nt = t; + _names = Names; + if((t & NameTypes.Regex) != NameTypes.None) + { + _regexString = new Regex[Names.Length]; + for(int i = 0; i < Names.Length; i++) + { + if(string.IsNullOrEmpty(Names[i])) + continue; + + try + { + _regexString[i] = new Regex(Names[i], (t & NameTypes.CaseInsensitive) != NameTypes.None ? RegexOptions.IgnoreCase : RegexOptions.None); + } + catch + { + // User entered invalid regex pattern + } + } + } + else if((t & NameTypes.CaseInsensitive) != NameTypes.None) + { + for(int i = 0; i < _names.Length; i++) + _names[i] = _names[i].ToLower(); + } + } + + private readonly Regex[] _regexString; + private readonly string[] _names; + private readonly NameTypes nt; + + public override bool IsTargetRoom(Room r) + { + if(string.IsNullOrEmpty(r.Name)) + return false; + + if((nt & NameTypes.Regex) != NameTypes.None) + { + foreach(Regex x in _regexString) + { + if(x == null) + continue; + + if(x.Match(r.Name).Success) + return true; + } + } + else + { + string rname = r.Name; + if((nt & NameTypes.CaseInsensitive) != NameTypes.None) + rname = rname.ToLower(); + foreach(string x in _names) + { + if((nt & NameTypes.Partial) != NameTypes.None) + { + if(rname.Contains(x)) + return true; + } + else if(rname == x) + return true; + } + } + + return false; + } + } + + [Flags] + public enum NameTypes + { + /// + /// Exact name search. Room name must match what you entered and case sensitive. + /// + None = 0, + + /// + /// Room name must contain the string you entered. This setting is ignored if you set regex option. + /// + Partial = 1, + + /// + /// Room name vs. what you entered is not case sensitive. + /// + CaseInsensitive = 2, + + /// + /// You entered a regex string which must match room name. + /// + Regex = 4, + } +} diff --git a/Mapper/Finders/Unmapped.cs b/Mapper/Finders/Unmapped.cs new file mode 100644 index 0000000..26b5ce7 --- /dev/null +++ b/Mapper/Finders/Unmapped.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Mapper +{ + public class Pathfinder_Unmapped : Pathfinder + { + public Pathfinder_Unmapped(bool unReconed) + : base() + { + UnReconed = unReconed; + } + + private bool UnReconed; + private uint[] AllowedAreas; + + public override void OnStartedPathfind() + { + base.OnStartedPathfind(); + + List a = new List(); + foreach(Room r in StartRooms) + { + if(r.Area.Entry == uint.MaxValue) + continue; + if(!a.Contains(r.Area.Entry)) + a.Add(r.Area.Entry); + } + + AllowedAreas = a.ToArray(); + } + + public override bool IsTargetRoom(Room r) + { + if(!AllowedAreas.Contains(r.Area.Entry)) + return false; + + foreach(Exit e in r.exits) + { + if(e.To.Area.Entry == uint.MaxValue) + return true; + } + + if(UnReconed) + { + if(!r.HasCustomFlag("reconed")) + return true; + } + + return base.IsTargetRoom(r); + } + } +} diff --git a/Mapper/Finders/VisitAll.cs b/Mapper/Finders/VisitAll.cs new file mode 100644 index 0000000..b251444 --- /dev/null +++ b/Mapper/Finders/VisitAll.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Mapper +{ + public class PathFinder_VisitAll : Pathfinder + { + public PathFinder_VisitAll(Mapper mapper, params Room[] roomId) + : base() + { + map = mapper; + foreach(Room u in roomId) + { + if(!AllRooms.Contains(u)) + AllRooms.Add(u); + } + + if(_grid[0] == null) + { + for(int i = 0; i < _grid.Length; i++) + _grid[i] = new List(512); + } + + for(int i = 0; i < _grid.Length; i++) + _grid[i].Clear(); + } + + private readonly Mapper map; + private readonly List AllRooms = new List(); + private readonly Dictionary> Costs = new Dictionary>(); + private static List[] _grid = new List[1024]; + + public override bool IsTargetRoom(Room r) + { + return true; + } + + public override void OnStartedPathfind() + { + base.OnStartedPathfind(); + + foreach(Room x in StartRooms) + AllRooms.Remove(x); + } + + public override void OnEndedPathFind(PathfindResult pr) + { + base.OnEndedPathFind(pr); + + foreach(Room x in StartRooms) + AllRooms.Add(x); + + foreach(Room u in AllRooms) + { + _Internal_PathFinder_VisitAll pf = new _Internal_PathFinder_VisitAll((from val in AllRooms where val != u select val).ToArray()); + pf.CanUsePortals = CanUsePortals; + pf.CanUseRecalls = CanUseRecalls; + pf.CharacterLevel = CharacterLevel; + pf.CharacterTier = CharacterTier; + pf.IsGlobalQuest = IsGlobalQuest; + pf.IsSingleClassTier0 = IsSingleClassTier0; + pf.OverridePortals = OverridePortals; + pf.SkipExits = SkipExits; + pf.SkipPortals = SkipPortals; + pf.SkipRooms = SkipRooms; + pf.StartRooms = new[] { u }; + + PathfindResult p = map.Get(pf); + if(!p.Success) + { + pr.Success = false; + return; + } + + Costs[u.Entry] = new Dictionary(); + foreach(Room x in AllRooms) + { + if(x == u) + continue; + + if(x.Mapper_OpenBy != null) + Costs[u.Entry][x.Entry] = x.Mapper_OpenCost; + else + { + pr.Success = false; + return; + } + } + } + + OpenRoom(new[] { StartRooms[0].Entry }, 0); + + uint[] Finish = null; + int itr1 = 0, itr2 = 0; + while(itr1 < _grid.Length) + { + if(itr2 >= _grid[itr1].Count) + { + itr2 = 0; + _grid[itr1].Clear(); + itr1++; + continue; + } + + if(OpenRoom(_grid[itr1][itr2], (uint)itr1)) + { + Finish = _grid[itr1][itr2]; + break; + } + + itr2++; + } + + if(Finish == null) + pr.Success = false; + else + { + pr.Cost = itr1; + pr.Path.Clear(); + pr.Target = map.GetRoom(Finish[Finish.Length - 1]); + Room u = map.GetRoom(Finish[0]); + int j = 1; + while(u != null && j < Finish.Length) + { + Pathfinder_Entry pf = new Pathfinder_Entry(Finish[j]); + pf.StartRooms = new[] { u }; + pf.CanUsePortals = CanUsePortals; + pf.CanUseRecalls = CanUseRecalls; + pf.CharacterLevel = CharacterLevel; + pf.CharacterTier = CharacterTier; + pf.IsGlobalQuest = IsGlobalQuest; + pf.IsSingleClassTier0 = IsSingleClassTier0; + pf.OverridePortals = OverridePortals; + pf.SkipExits = SkipExits; + pf.SkipPortals = SkipPortals; + pf.SkipRooms = SkipRooms; + + PathfindResult p = map.Get(pf); + if(!p.Success) + { + pr.Success = false; + return; + } + + pr.Path.AddRange(p.Path); + u = map.GetRoom(Finish[j]); + j++; + } + } + } + + private bool OpenRoom(uint[] r, uint cost) + { + if(r.Length == AllRooms.Count) + return true; + + foreach(KeyValuePair x in Costs[r[r.Length - 1]]) + { + if(r.Contains(x.Key)) + continue; + + uint[] z = r.Concat(new[] { x.Key }).ToArray(); + uint c = cost + x.Value; + if(c < _grid.Length) + _grid[(int)c].Add(z); + } + + return false; + } + } + + public class _Internal_PathFinder_VisitAll : Pathfinder + { + public _Internal_PathFinder_VisitAll(params Room[] roomId) + { + AllRooms.AddRange(roomId); + } + + private readonly List AllRooms = new List(); + + public override bool IsTargetRoom(Room r) + { + AllRooms.Remove(r); + return AllRooms.Count == 0; + } + } +} diff --git a/Mapper/Finders/desktop.ini b/Mapper/Finders/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Finders/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Mapper.cs b/Mapper/Mapper.cs new file mode 100644 index 0000000..dfedf9c --- /dev/null +++ b/Mapper/Mapper.cs @@ -0,0 +1,1607 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Mapper.Scripting; +using ProxyCore; +using ProxyCore.Input; +using ProxyCore.Output; +using ProxyCore.Scripting; +using System.IO; +using System.Runtime.Serialization; + +namespace Mapper +{ + public partial class Mapper : Plugin + { + public Mapper() + : base("mapper", "Mapper") + { + Author = "Duckbat"; + Version = 15; + Description = "This plugin will record what rooms you have been in and from that can generate speedwalks for you to easily move around the world of Aardwolf."; + UpdateUrl = "www.duckbat.com/plugins/update.mapper.txt"; + Website = "code.google.com/p/proxymud/"; + + Config = new MapperConfig(); + + RegisterCommand("pm", "", CommandMapper); + RegisterCommand("all", @"(.+)", CommandAll, 0, CMDFlags.None, "pm"); + RegisterCommand("count", "", CommandCount, 0, CMDFlags.None, "pm"); + RegisterCommand("createexit", "^(\\d+)(\\s+\\d+)?\\s+\"(.+)\"$", CommandCreateExit, 0, CMDFlags.None, "pm"); + RegisterCommand("createportal", "^(\\d+)\\s+\"(.+)\"$", CommandCreatePortal, 0, CMDFlags.None, "pm"); + RegisterCommand("delete", @"^(area|room|exit|portal)\s+(\d+)", CommandDelete, 3, CMDFlags.None, "pm"); + RegisterCommand("exits", @"^(help)?(room\s+\d+)?(\d+)?(\s+.+)?", CommandExit, 4, CMDFlags.None, "pm"); + RegisterCommand("find", @"^(room|area)(\s+exact)?(\s+case)?\s+(.+)", CommandFind, 1, CMDFlags.None, "pm"); + RegisterCommand("portals", "", CommandPortal, 4, CMDFlags.None, "pm"); + RegisterCommand("goto", @"(.+)", CommandGoto, 2, CMDFlags.None, "pm"); + RegisterCommand("import", @"(.+)", CommandImport, 0, CMDFlags.None, "pm"); + RegisterCommand("roominfo", @"^(help)?(\d+)?(\s+.+)?", CommandRoomInfo, 4, CMDFlags.None, "pm"); + RegisterCommand("save", @"(.+)", CommandSave, 4, CMDFlags.None, "pm"); + RegisterCommand("unmapped", @"^(go|all)$", CommandUnmapped, 2, CMDFlags.None, "pm"); + RegisterCommand("unreconed", @"^(go|all)$", CommandUnreconed, 3, CMDFlags.None, "pm"); + + RegisterTrigger("room.id", @"^\$gmcp\.room\.info\.num (-?\d+)$", TriggerRoomInfoNum); + RegisterTrigger("room.name", @"^\$gmcp\.room\.info\.name (.*)$", TriggerRoomInfoName); + RegisterTrigger("room.area", @"^\$gmcp\.room\.info\.zone (.*)$", TriggerRoomInfoArea); + RegisterTrigger("room.exit", @"^\$gmcp\.room\.info\.exits\.(\w) (-?\d+)$", TriggerRoomInfoExits); + RegisterTrigger("room.finish", @"^\$gmcp\.room\.info\.coords?\.", TriggerRoomInfoFinish); + RegisterTrigger("char.level", @"^\$gmcp\.char\.status\.level (\d+)$", TriggerCharStatusLevel); + RegisterTrigger("char.tier", @"^\$gmcp\.char\.base\.tier (\d+)$", TriggerCharBaseTier); + RegisterTrigger("char.remorts", @"^\$gmcp\.char\.base\.remorts (\d+)$", TriggerCharBaseRemorts); + RegisterTrigger("gq.join", @"@wYou have now joined the quest. See 'help gquest' for available commands.", + TriggerJoinedGQ, TriggerFlags.NotRegex); + RegisterTrigger("gq.left", @"@wYou are no longer part of the current quest.", TriggerLeftGQ, + TriggerFlags.NotRegex); + RegisterTrigger("gq.win", + @"^@RGlobal Quest@Y: @wThe global quest has been won by @Y\w+ @w- @Y\d+.. @wwin\.$", + TriggerLeftGQ); + RegisterTrigger("gq.quit", @"@wYou are no longer part of the current quest.", TriggerLeftGQ, TriggerFlags.NotRegex); + RegisterTrigger("where.name", @"^@GYou are in area : (.+)", TriggerWhereName); + RegisterTrigger("where.level", @"^@GLevel range is : @R(\d+) to (\d+)", TriggerWhereLevel); + RegisterTrigger("areas.start", @"^@WFrom To Lock Keyword Area Name", TriggerAreasStart); + RegisterTrigger("areas.end", @"@w---------------------------------------------------------------", TriggerAreasEnd, TriggerFlags.NotRegex); + RegisterTrigger("areas.entry", @"^\s+@w(\d+)\s+(\d+)\s+(@R\d+\s+)?@g([\d\w]+)\s+@c(.+)", TriggerAreasEntry); + RegisterTrigger("home", @"@wYou cannot return home from this room.", TriggerNoRecall, TriggerFlags.NotRegex); + RegisterTrigger("recall", @"@wYou cannot recall from this room.", TriggerNoRecall, TriggerFlags.NotRegex); + RegisterTrigger("portal", @"@wMagic walls bounce you back.", TriggerPrison, TriggerFlags.NotRegex); + RegisterTrigger("recon.area", @"^@wArea Name : (.+)", TriggerReconArea); + RegisterTrigger("recon.sector", @"^@wSector type is : (.+)", TriggerReconSector); + RegisterTrigger("recon.flags", @"^@wBase flags :(.*)", TriggerReconFlags); + RegisterTrigger("recon.healrate", @"^@wHeal rate : @c(-?\d+)", TriggerReconHealRate); + RegisterTrigger("recon.manarate", @"^@wMana rate : @c(-?\d+)", TriggerReconManaRate); + RegisterTrigger("recon.unable", "@wYou cannot perform reconnaissance in this room.", TriggerNoRecon, TriggerFlags.NotRegex); + RegisterTrigger("tags.exits", @"^(@w)?\{exits\}(.*)", TriggerTagsExits); + + Load(); + + // If we don't have this unmapped area set then create it, just so we can save rooms we haven't explored yet + if(GetArea(uint.MaxValue) == null) + { + Area a = new Area(uint.MaxValue); + a.Keyword = "mapper_unmapped"; + a.Name = "Mapper unmapped rooms"; + IAreas[a.Entry] = a; + } + + Path_Init(); + } + + private const string DBFileName = "mapperdb.xml"; + private const string DBFileBackup = "mapperdb_backup.xml"; + + private int Level = 1; + private int Tier = 0; + private bool ListenArea = false; + private int Remorts = 1; + private bool HasGQ = false; + private long WhenSave = 0; + + private void OnDeleted(Room r) + { + List del = new List(); + foreach(KeyValuePair x in IExits) + { + if(x.Key == uint.MaxValue || x.Key == 0) + continue; + if(!IRooms.ContainsKey(x.Value.ToRoom)) + del.Add(x.Key); + } + + foreach(uint x in del) + { + IExits[x].From.exits.Remove(IExits[x]); + IExits.Remove(x); + } + } + + /// + /// Fill a pathfinder with our current character values and return if we were successful in doing that. + /// + /// + public bool FillPathFinder(Pathfinder p) + { + Room r; + if(CurrentRoomId == uint.MaxValue || (r = GetRoom(CurrentRoomId)) == null || r.Area.Entry == uint.MaxValue) + return false; + p.CharacterLevel = Level; + p.CharacterTier = Tier; + p.CanUseRecalls = true; + p.CanUsePortals = true; + p.IsGlobalQuest = HasGQ; + p.IsSingleClassTier0 = Tier == 0 && Remorts == 1; + p.StartRooms = new[] { r }; + return true; + } + + #region Commands + private bool CommandAll(InputData i) + { + string[] spl; + if(!i.Arguments.Success || (spl = i.Arguments.Groups[1].Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)).Length == 0) + { + World.Instance.SendMessage("@wSyntax: map all [room id] ...", i.ClientMask); + return true; + } + + List rooms = new List(); + bool hadConfirm = false; + foreach(string x in spl) + { + uint u; + if(uint.TryParse(x, out u)) + { + Room r = GetRoom(u); + if(r != null && !rooms.Contains(r)) + rooms.Add(r); + } + else if(x.ToLower() == "confirm") + hadConfirm = true; + } + + if(rooms.Count < 2) + { + World.Instance.SendMessage("@wSyntax: map all [room id] ...", i.ClientMask); + return true; + } + + if(rooms.Count > 10 && !hadConfirm) + { + World.Instance.SendMessage("@RWarning! @wEntered more than 10 rooms. This may be too slow to finish. Enter confirm at the end if you are sure you wish to do this pathfind.", i.ClientMask); + return true; + } + + int ms = Environment.TickCount; + PathFinder_VisitAll pf = new PathFinder_VisitAll(this, rooms.ToArray()); + FillPathFinder(pf); + PathfindResult pr = Get(pf); + ms = Environment.TickCount - ms; + + World.Instance.SendMessage("@wPathfind took @W" + ms + " @wms.", i.ClientMask); + if(!pr.Success) + { + World.Instance.SendMessage("@wPathfind failed.", i.ClientMask); + return true; + } + + string sw = PathfindResult.Speedwalk(pr.Path); + World.Instance.SendMessage("@wCost: @W" + pr.Cost, i.ClientMask); + World.Instance.SendMessage("@wSW: " + sw, i.ClientMask); + return true; + } + + private bool CommandImport(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: map import ", i.ClientMask); + return true; + } + + StreamReader f; + try + { + f = new StreamReader(i.Arguments.Groups[1].Value); + } + catch + { + World.Instance.SendMessage("@wFailed opening file. Make sure it is in the same folder as ProxyMud.exe and make sure you entered the file extension too.", i.ClientMask); + return true; + } + + string l; + while((l = f.ReadLine()) != null) + { + if(l.StartsWith("{area}")) + { + l = l.Substring(6); + string key = l.Substring(0, l.IndexOf('\t')).Trim().ToLower(); + l = l.Substring(l.IndexOf('\t') + 1); + + if(string.IsNullOrEmpty(key)) + continue; + + if(GetArea(key) == null) + { + Area a = new Area(++_guidArea); + a.Name = l; + a.Keyword = key; + IAreas[a.Entry] = a; + } + else if(string.IsNullOrEmpty(GetArea(key).Name)) + GetArea(key).Name = l; + } + else if(l.StartsWith("{room}")) + { + l = l.Substring(6); + string key = l.Substring(0, l.IndexOf('\t')); + uint k; + if(!uint.TryParse(key, out k)) + continue; + + l = l.Substring(l.IndexOf('\t') + 1); + string name = l.Substring(0, l.IndexOf('\t')); + + l = l.Substring(l.IndexOf('\t') + 1).Trim().ToLower(); + Area a = GetArea(l); + if(a == null) + { + a = new Area(++_guidArea); + a.Keyword = l; + IAreas[a.Entry] = a; + } + + Room r = GetRoom(k); + if(r == null) + { + r = new Room(k); + IRooms[k] = r; + r.Name = name; + r.Area = a; + } + else + { + if(r.Area != a) + r.Area = a; + r.Name = name; + } + } + else if(l.StartsWith("{exit}")) + { + l = l.Substring(6); + string cmd = l.Substring(0, l.IndexOf('\t')); + l = l.Substring(l.IndexOf('\t') + 1); + + string fr = l.Substring(0, l.IndexOf('\t')); + l = l.Substring(l.IndexOf('\t') + 1); + + string tr = l.Substring(0, l.IndexOf('\t')); + l = l.Substring(l.IndexOf('\t') + 1); + + uint from; + uint to; + int minLevel; + + if(!uint.TryParse(fr, out from) || !uint.TryParse(tr, out to) || !int.TryParse(l, out minLevel)) + continue; + + Room fromroom = GetRoom(from); + Room toroom = GetRoom(to); + if(fromroom == null || toroom == null) + continue; + + string door = ""; + if(cmd.Contains(';')) + { + door = cmd.Substring(0, cmd.IndexOf(';')); + if(!door.StartsWith("open ") && !door.StartsWith("ope ") && !door.StartsWith("op ") && + !door.StartsWith("o ")) + door = ""; + else + { + door = door.Substring(door.IndexOf(' ') + 1).Trim(); + char dir = PathfindResult.IsDirectionCommand(door); + if(dir == 'x') + door = "open " + door; + else + door = "o"; + cmd = cmd.Substring(cmd.IndexOf(';') + 1); + } + } + + Exit e = null; + if(PathfindResult.IsDirectionCommand(cmd) != 'x') + e = fromroom.GetExit(PathfindResult.IsDirectionCommand(cmd)); + else + e = fromroom.GetExit(cmd); + if(e == null) + { + e = new Exit(++_guidExit); + e.Command = PathfindResult.IsDirectionCommand(cmd) != 'x' ? PathfindResult.IsDirectionCommand(cmd).ToString() : cmd; + e.From = fromroom; + e.To = toroom; + e.From.exits.Add(e); + e.From.UpdateExits(); + IExits[e.Entry] = e; + + if(door == "o") + e.AddFlag("door"); + else if(!string.IsNullOrEmpty(door)) + e.DoorCommand = door; + } + } + else if(l.Length == 0) + continue; + else + { + f.Close(); + World.Instance.SendMessage("@wInvalid mapper format. You must convert it to readable format with the converter.", i.ClientMask); + return true; + } + } + + f.Close(); + World.Instance.SendMessage("@wDone importing missing rooms / areas / exits from MUSH database.", i.ClientMask); + return true; + } + + private bool CommandSave(InputData i) + { + string fileName = DBFileName; + if(i.Arguments.Success) + fileName = i.Arguments.Groups[0].Value; + + Save(fileName); + World.Instance.SendMessage("@wSaved mapper database to '@W" + fileName + "@w'.", i.ClientMask); + return true; + } + + private bool CommandUnmapped(InputData i) + { + if(!i.Arguments.Success) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + { + World.Instance.SendMessage("@wYou are in an unkown room.", i.ClientMask); + return true; + } + + uint c = 0; + foreach(KeyValuePair x in IRooms) + { + if(x.Value.Area.Entry != r.Area.Entry) + continue; + + bool f = false; + foreach(Exit e in x.Value.exits) + { + if(e.To.Area.Entry == uint.MaxValue) + { + f = true; + break; + } + } + + if(f) + c++; + } + + if(c == 0) + { + World.Instance.SendMessage("@wDidn't find any rooms with exits to unmapped rooms in this area.", + i.ClientMask); + } + else + { + World.Instance.SendMessage( + "@wFound @C" + c + " @wroom" + (c != 1 ? "s" : "") + + " in this area that have exits to unmapped rooms.", i.ClientMask); + } + World.Instance.SendMessage("@wUse '@Wmap unmapped all@w' to see areas where exits to unmapped rooms are found.", i.ClientMask); + World.Instance.SendMessage("@wUse '@Wmap unmapped go@w' to go to the closest room where an exit to unmapped room is found (in current area only).", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[1].Value.ToLower() == "all") + { + SortedDictionary> Unmapped = new SortedDictionary>(); + foreach(KeyValuePair x in IRooms) + { + bool f = false; + foreach(Exit e in x.Value.exits) + { + if(e.To.Area.Entry == uint.MaxValue) + { + f = true; + break; + } + } + + if(f) + { + string n = x.Value.Area.Name; + if(string.IsNullOrEmpty(n)) + n = x.Value.Area.Keyword; + if(!Unmapped.ContainsKey(n)) + Unmapped[n] = new Dictionary(); + if(!Unmapped[n].ContainsKey(x.Value.Area)) + Unmapped[n][x.Value.Area] = 1; + else + Unmapped[n][x.Value.Area]++; + } + } + + if(Unmapped.Count == 0) + World.Instance.SendMessage("@wDidn't find any rooms in any area that have exits to unmapped rooms.", i.ClientMask); + else + { + World.Instance.SendMessage("@WEntry Area Unmapped rooms", i.ClientMask); + World.Instance.SendMessage("@G===== ============================================ ==============", i.ClientMask); + int c = 0; + foreach(KeyValuePair> x in Unmapped) + { + foreach(KeyValuePair y in x.Value) + { + c++; + World.Instance.SendMessage("@Y" + string.Format("{0,-5}", y.Key.Entry) + " @M" + string.Format("{0,-" + "============================================".Length + "}", (!string.IsNullOrEmpty(y.Key.Name) ? y.Key.Name : y.Key.Keyword)) + " @C" + y.Value, i.ClientMask); + } + } + + World.Instance.SendMessage("@wFound @C" + c + " @warea" + (c != 1 ? "s" : "") + " with exits to unmapped rooms.", i.ClientMask); + } + return true; + } + + if(GetRoom(CurrentRoomId) == null) + { + World.Instance.SendMessage("@wYou are in an invalid room.", i.ClientMask); + return true; + } + + Pathfinder_Unmapped p = new Pathfinder_Unmapped(false); + FillPathFinder(p); + PathfindResult pr = Get(p); + if(!pr.Success) + { + World.Instance.SendMessage("@wCouldn't find a path or there weren't any rooms in this area with exits that lead to unmapped rooms.", i.ClientMask); + return true; + } + Goto(pr); + return true; + } + + private bool CommandUnreconed(InputData i) + { + if(!i.Arguments.Success) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + { + World.Instance.SendMessage("@wYou are in an unknown room.", i.ClientMask); + return true; + } + + uint c = 0; + foreach(KeyValuePair x in IRooms) + { + if(x.Value.Area.Entry != r.Area.Entry) + continue; + + bool f = false; + foreach(Exit e in x.Value.exits) + { + if(e.To.Area.Entry == uint.MaxValue) + { + f = true; + break; + } + } + if(!f && !x.Value.HasCustomFlag("reconed")) + f = true; + + if(f) + c++; + } + + if(c == 0) + { + World.Instance.SendMessage("@wDidn't find any rooms with exits to unmapped rooms or unreconed in this area.", + i.ClientMask); + } + else + { + World.Instance.SendMessage( + "@wFound @C" + c + " @wroom" + (c != 1 ? "s" : "") + + " in this area that have exits to unmapped rooms or aren't reconed.", i.ClientMask); + } + World.Instance.SendMessage("@wUse '@Wmap unreconed all@w' to see areas where exits to unmapped rooms or rooms that aren't reconed are found.", i.ClientMask); + World.Instance.SendMessage("@wUse '@Wmap unreconed go@w' to go to the closest room where an exit to unmapped room is found or that isn't reconed (in current area only).", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[1].Value.ToLower() == "all") + { + SortedDictionary> Unmapped = new SortedDictionary>(); + foreach(KeyValuePair x in IRooms) + { + bool f = false; + foreach(Exit e in x.Value.exits) + { + if(e.To.Area.Entry == uint.MaxValue) + { + f = true; + break; + } + } + if(!f && !x.Value.HasCustomFlag("reconed")) + f = true; + + if(f) + { + string n = x.Value.Area.Name; + if(string.IsNullOrEmpty(n)) + n = x.Value.Area.Keyword; + if(!Unmapped.ContainsKey(n)) + Unmapped[n] = new Dictionary(); + if(!Unmapped[n].ContainsKey(x.Value.Area)) + Unmapped[n][x.Value.Area] = 1; + else + Unmapped[n][x.Value.Area]++; + } + } + + if(Unmapped.Count == 0) + World.Instance.SendMessage("@wDidn't find any rooms in any area that have exits to unmapped rooms or that aren't reconed.", i.ClientMask); + else + { + World.Instance.SendMessage("@WEntry Area Unmapped/Unreconed rooms", i.ClientMask); + World.Instance.SendMessage("@G===== ============================================ ========================", i.ClientMask); + int c = 0; + foreach(KeyValuePair> x in Unmapped) + { + foreach(KeyValuePair y in x.Value) + { + c++; + World.Instance.SendMessage("@Y" + string.Format("{0,-5}", y.Key.Entry) + " @M" + string.Format("{0,-" + "============================================".Length + "}", (!string.IsNullOrEmpty(y.Key.Name) ? y.Key.Name : y.Key.Keyword)) + " @C" + y.Value, i.ClientMask); + } + } + + World.Instance.SendMessage("@wFound @C" + c + " @warea" + (c != 1 ? "s" : "") + " with exits to unmapped rooms or unreconed rooms.", i.ClientMask); + } + return true; + } + + if(GetRoom(CurrentRoomId) == null) + { + World.Instance.SendMessage("@wYou are in an invalid room.", i.ClientMask); + return true; + } + + Pathfinder_Unmapped p = new Pathfinder_Unmapped(true); + FillPathFinder(p); + PathfindResult pr = Get(p); + if(!pr.Success) + { + World.Instance.SendMessage("@wCouldn't find a path or there weren't any rooms in this area with exits that lead to unmapped rooms or that aren't reconed.", i.ClientMask); + return true; + } + Goto(pr); + return true; + } + + private bool CommandDelete(InputData i) + { + // @"^(area|room|exit|portal)\s+(\d+)" + uint id; + if(!i.Arguments.Success || !uint.TryParse(i.Arguments.Groups[2].Value, out id) || id == uint.MaxValue) + { + World.Instance.SendMessage("@wSyntax: map delete area ", i.ClientMask); + World.Instance.SendMessage(" @wmap delete exit ", i.ClientMask); + World.Instance.SendMessage(" @wmap delete portal ", i.ClientMask); + World.Instance.SendMessage(" @wmap delete room ", i.ClientMask); + return true; + } + + string w = i.Arguments.Groups[1].Value.ToLower(); + + switch(w) + { + case "area": + { + Area a = GetArea(id); + if(a == null) + { + World.Instance.SendMessage("@wNo such area (@R" + id + "@w).", i.ClientMask); + return true; + } + + IAreas.Remove(a.Entry); + foreach(Room r in a.rooms) + { + IRooms.Remove(r.Entry); + foreach(Exit e in r.exits) + IExits.Remove(e.Entry); + OnDeleted(r); + } + + if(!string.IsNullOrEmpty(a.Name)) + World.Instance.SendMessage("@wDeleted area '@M" + a.Name + "@w'.", i.ClientMask); + else + World.Instance.SendMessage("@wDeleted area '@w" + a.Keyword + "@w'.", i.ClientMask); + } break; + + case "exit": + case "portal": + { + Exit e = GetExit(id); + if(e == null) + e = IPortals.ContainsKey(id) ? IPortals[id] : null; + if(e == null) + { + World.Instance.SendMessage("@wNo such exit or portal (@R" + id + "@w).", i.ClientMask); + return true; + } + + IExits.Remove(e.Entry); + IPortals.Remove(e.Entry); + if(!e.HasFlag("portal")) + e.From.exits.Remove(e); + else + e.To.Area.Portals.Remove(e); + + World.Instance.SendMessage("@wDeleted exit '@Y" + e.Entry + "@w'.", i.ClientMask); + } break; + + case "room": + { + Room r = GetRoom(id); + if(r == null) + { + World.Instance.SendMessage("@wNo such room (@R" + id + "@w).", i.ClientMask); + return true; + } + + IRooms.Remove(r.Entry); + r.Area.rooms.Remove(r); + OnDeleted(r); + World.Instance.SendMessage("@wDeleted room '@G" + r.Name + "@w'.", i.ClientMask); + } break; + } + + return true; + } + + private bool CommandFind(InputData i) + { + // @"^(room|area)(\s+exact)?(\s+case)?\s+(.+)" + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: map find room [exact] [case] ", i.ClientMask); + World.Instance.SendMessage(" @wmap find area [exact] [case] ", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[1].Value.ToLower() == "room") + { + string Str = i.Arguments.Groups[4].Value; + if(i.Arguments.Groups[3].Length == 0) + Str = Str.ToLower(); + List Found = new List(); + foreach(KeyValuePair x in IRooms) + { + string Name = x.Value.Name; + if(string.IsNullOrEmpty(Name)) + continue; + if(i.Arguments.Groups[3].Length == 0) + Name = Name.ToLower(); + + if(i.Arguments.Groups[2].Length != 0) + { + if(Name == Str) + Found.Add(x.Value); + } + else if(Name.Contains(Str)) + Found.Add(x.Value); + } + + if(Found.Count == 0) + World.Instance.SendMessage("@wFound nothing!", i.ClientMask); + else + { + World.Instance.SendMessage("@WEntry Name Area", i.ClientMask); + World.Instance.SendMessage("@G====== ================================ ===============================", i.ClientMask); + foreach(Room f in Found) + { + World.Instance.SendMessage("@Y" + string.Format("{0,6}", f.Entry) + " @G" + Utility.FormatColoredString(f.Name, -"================================".Length) + " " + (!string.IsNullOrEmpty(f.Area.Name) ? ("@M" + f.Area.Name) : ("@w" + f.Area.Keyword)), i.ClientMask); + } + World.Instance.SendMessage("@wFound @C" + Found.Count + " @wroom" + (Found.Count != 1 ? "s" : "") + ".", i.ClientMask); + } + } + else + { + string Str = i.Arguments.Groups[4].Value; + if(i.Arguments.Groups[3].Length == 0) + Str = Str.ToLower(); + List Found = new List(); + foreach(KeyValuePair x in IAreas) + { + string Name = x.Value.Name; + if(string.IsNullOrEmpty(Name)) + continue; + if(i.Arguments.Groups[3].Length == 0) + Name = Name.ToLower(); + + if(i.Arguments.Groups[2].Length != 0) + { + if(Name == Str) + Found.Add(x.Value); + } + else if(Name.Contains(Str)) + Found.Add(x.Value); + } + + if(Found.Count == 0) + World.Instance.SendMessage("@wFound nothing!", i.ClientMask); + else + { + World.Instance.SendMessage("@WEntry Keyword Name", i.ClientMask); + World.Instance.SendMessage("@G====== ========== ========================================", i.ClientMask); + foreach(Area f in Found) + { + World.Instance.SendMessage("@Y" + string.Format("{0,6}", f.Entry) + " @w" + Utility.FormatColoredString(f.Keyword, -10) + " @M" + f.Name, i.ClientMask); + } + World.Instance.SendMessage("@wFound @C" + Found.Count + " @warea" + (Found.Count != 1 ? "s" : "") + ".", i.ClientMask); + } + } + return true; + } + + private bool CommandMapper(InputData i) + { + World.Instance.SendMessage("@wAvailable mapper commands:", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map all") + " @w- Shows speedwalk that runs through all the rooms (IDs) you entered starting from your current location.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map count") + " @w- Shows how many rooms you have mapped.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map createexit") + " @w- Create a new exit.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map createportal") + " @w- Create a new portal.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map delete") + " @w- Delete a room, area or an exit.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map exits") + " @w- Show exits information or edit them.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map find") + " @w- Find rooms or areas in mapper.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map goto") + " @w- Goto a room or an area.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map import") + " @w- Import data file converted from mush mapper.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map portals") + " @w- List all portals.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map roominfo") + " @w- Show more information about a room or edit it.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map save") + " @w- Save the mapper database now. Enter argument to save to another file.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map unmapped") + " @w- Find unmapped rooms.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "map unreconed") + " @w- Find unmapped or unreconed rooms.", i.ClientMask); + return true; + } + + private bool CommandGoto(InputData i) + { + if(!i.Arguments.Success || i.Arguments.Groups[1].Value.Trim().Length == 0) + { + World.Instance.SendMessage("@wGo to the closest room entered.", i.ClientMask); + World.Instance.SendMessage("@wSyntax: map goto [room id] [room id]", i.ClientMask); + World.Instance.SendMessage(" @wmap goto ", i.ClientMask); + World.Instance.SendMessage(" @wmap goto ", i.ClientMask); + return true; + } + + if(CurrentRoomId == uint.MaxValue || GetRoom(CurrentRoomId) == null) + { + World.Instance.SendMessage("@wWe are in an unknown room.", i.ClientMask); + return true; + } + + List roomId = new List(); + Area aTarget = null; + string[] testIds = i.Arguments.Groups[1].Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string x in testIds) + { + uint u; + if(uint.TryParse(x, out u)) + { + if(GetRoom(u) == null) + { + World.Instance.SendMessage("@wNo such room (@R" + u + "@w)!", i.ClientMask); + continue; + } + if(!roomId.Contains(u)) + roomId.Add(u); + } + else + { + roomId = null; + break; + } + } + + if(roomId == null) + aTarget = GetArea(i.Arguments.Groups[1].Value); + + Pathfinder e; + if(roomId != null) + e = new Pathfinder_Entry(roomId.ToArray()); + else if(aTarget != null) + { + if(aTarget.StartRoom != 0) + e = new Pathfinder_Entry(aTarget.StartRoom); + else + e = new Pathfinder_Area(aTarget.Entry); + } + else + e = new Pathfinder_Name(NameTypes.Partial | NameTypes.CaseInsensitive, i.Arguments.Groups[1].Value); + + FillPathFinder(e); + PathfindResult pr = Get(e); + if(!pr.Success) + { + World.Instance.SendMessage("@wCouldn't find a path to any of the rooms entered.", i.ClientMask); + return true; + } + + if(aTarget != null) + World.Instance.SendMessage("@wGoing to area '@C" + pr.Target.Area.Name + "@w'...", i.ClientMask); + else + World.Instance.SendMessage("@wGoing to room '@G" + pr.Target.Name + "@w'...", i.ClientMask); + + Goto(pr); + return true; + } + + private bool CommandCount(InputData i) + { + int thisArea = 0; + int total = 0; + foreach(KeyValuePair x in IRooms) + { + if(x.Value.Area.Keyword == RoomInfoArea) + thisArea++; + if(x.Value.Area.Entry != uint.MaxValue) + total++; + } + World.Instance.SendMessage("@wYou have mapped @G" + thisArea + " @wrooms in this area.", i.ClientMask); + World.Instance.SendMessage("@wYou have @G" + (IAreas.Count - 1) + " @wareas in mapper.", i.ClientMask); + World.Instance.SendMessage("@wYou have mapped @G" + total + " @wrooms of Aardwolf.", i.ClientMask); + return true; + } + + #endregion + + #region Triggers + private bool TriggerNoRecon(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r != null) + { + r.AddCustomFlag("reconed"); + r.AddCustomFlag("norecon"); + } + return false; + } + + private bool TriggerTagsExits(TriggerData t) + { + if(Config.GetInt32("Tags.Exits", 1) == 0) + return false; + + Room r = GetRoom(CurrentRoomId); + + StringBuilder str = new StringBuilder(); + string[] Exits = t.Match.Groups[2].Value.ToLower().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + CheckExit("north", 'n', Exits.Contains("north") || Exits.Contains("(north)"), Exits.Contains("(north)"), RoomInfoExits.ContainsKey('n') ? RoomInfoExits['n'] : 0, str); + CheckExit("east", 'e', Exits.Contains("east") || Exits.Contains("(east)"), Exits.Contains("(east)"), RoomInfoExits.ContainsKey('e') ? RoomInfoExits['e'] : 0, str); + CheckExit("south", 's', Exits.Contains("south") || Exits.Contains("(south)"), Exits.Contains("(south)"), RoomInfoExits.ContainsKey('s') ? RoomInfoExits['s'] : 0, str); + CheckExit("west", 'w', Exits.Contains("west") || Exits.Contains("(west)"), Exits.Contains("(west)"), RoomInfoExits.ContainsKey('w') ? RoomInfoExits['w'] : 0, str); + CheckExit("up", 'u', Exits.Contains("up") || Exits.Contains("(up)"), Exits.Contains("(up)"), RoomInfoExits.ContainsKey('u') ? RoomInfoExits['u'] : 0, str); + CheckExit("down", 'd', Exits.Contains("down") || Exits.Contains("(down)"), Exits.Contains("(down)"), RoomInfoExits.ContainsKey('d') ? RoomInfoExits['d'] : 0, str); + + if(r != null) + { + bool hadCustom = false; + foreach(Exit e in r.exits) + { + if(PathfindResult.IsDirectionCommand(e.Command) != 'x') + continue; + + hadCustom = true; + if(str.Length > 0) + str.Append(" "); + str.Append("@w'@C" + e.Command + "@w'"); + } + + if(!hadCustom && Exits.Contains("custom")) + { + if(str.Length > 0) + str.Append(" "); + str.Append("@Rcustom"); + } + } + + if(str.Length == 0) + str.Append("@Gnone"); + t.Msg.Msg = "@g[Exits: " + str.ToString() + "@g]"; + return false; + } + + private void CheckExit(string Word, char Dir, bool HasExits, bool IsClosed, uint LeadTo, StringBuilder str) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + { + if(!HasExits) + return; + + if(str.Length > 0) + str.Append(" "); + if(LeadTo == uint.MaxValue) + str.Append("@Y"); + else + str.Append("@G"); + str.Append(IsClosed ? ("(" + Word + ")") : Word); + return; + } + + if(LeadTo == uint.MaxValue) + { + if(str.Length > 0) + str.Append(" "); + str.Append("@Y"); + str.Append(IsClosed ? ("(" + Word + ")") : Word); + return; + } + + Exit e = r.GetExit(Dir); + if(e == null) + return; + + if(!HasExits) + { + if(e.HasFlag("hidden")) + { + if(str.Length > 0) + str.Append(" "); + str.Append("@y(" + Word + ")"); + return; + } + if(str.Length > 0) + str.Append(" "); + str.Append("@D(" + Word + ")"); + return; + } + + if(str.Length > 0) + str.Append(" "); + if(e.To.Area.Entry == uint.MaxValue) + str.Append("@R"); + else if(!e.To.HasCustomFlag("reconed") && Config.GetInt32("Tags.Exits.Recon", 0) != 0) + str.Append("@r"); + else + str.Append("@G"); + + if(IsClosed) + str.Append("(" + Word + ")"); + else + str.Append(Word); + } + + private bool TriggerNoRecall(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r != null) + r.AddFlag("norecall"); + return false; + } + + private bool TriggerPrison(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r != null) + r.AddFlag("prison"); + return false; + } + + private bool TriggerReconArea(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + return false; + string msg = Colors.RemoveColors(t.Match.Groups[1].Value, false); + string name = msg.Substring(0, msg.LastIndexOf('(')).Trim(); + msg = msg.Substring(msg.LastIndexOf('(') + 1); + + r.Area.Name = name; + try + { + int min, max; + if(int.TryParse(msg.Substring(0, msg.IndexOf(' ')), out min)) + r.Area.MinLevel = min; + msg = msg.Substring(msg.IndexOf("to ") + 3); + if(int.TryParse(msg.Substring(0, msg.IndexOf(' ')), out max)) + r.Area.MaxLevel = max; + } + catch + { + } + r.AddCustomFlag("reconed"); + return false; + } + + private bool TriggerReconSector(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + return false; + + r.Sector = Colors.RemoveColors(t.Match.Groups[1].Value, false); + return false; + } + + private bool TriggerReconHealRate(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + return false; + + int rate; + if(int.TryParse(t.Match.Groups[1].Value, out rate)) + r.HealRate = rate; + return false; + } + + private bool TriggerReconManaRate(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + return false; + + int rate; + if(int.TryParse(t.Match.Groups[1].Value, out rate)) + r.ManaRate = rate; + return false; + } + + private bool TriggerReconFlags(TriggerData t) + { + Room r = GetRoom(CurrentRoomId); + if(r == null) + return false; + + string[] fl = Colors.RemoveColors(t.Match.Groups[1].Value, false).Trim().ToLower().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if(r.IFlags != null) + r.IFlags.Clear(); + foreach(string x in fl) + r.AddFlag(x.Trim()); + return false; + } + + private bool TriggerAreasStart(TriggerData t) + { + ListenArea = true; + return false; + } + + private bool TriggerAreasEnd(TriggerData t) + { + ListenArea = false; + return false; + } + + private bool TriggerAreasEntry(TriggerData t) + { + if(!ListenArea) + return false; + + int minLevel, maxLevel, levelLock = 0; + + if(!int.TryParse(t.Match.Groups[1].Value, out minLevel) || + !int.TryParse(t.Match.Groups[2].Value, out maxLevel)) + return false; + + if(t.Match.Groups[3].Length != 0) + { + if(!int.TryParse(t.Match.Groups[3].Value, out levelLock)) + levelLock = 0; + } + + string keyWord = t.Match.Groups[4].Value; + string areaName = t.Match.Groups[5].Value.Trim(); + + Area a = GetArea(keyWord); + if(a == null) + { + a = new Area(++_guidArea); + IAreas[a.Entry] = a; + } + + a.Keyword = keyWord; + a.LevelLock = levelLock; + a.MaxLevel = maxLevel; + a.MinLevel = minLevel; + if(string.IsNullOrEmpty(a.Name) || a.Name.Length <= areaName.Length || !a.Name.StartsWith(areaName)) + a.Name = areaName; + return false; + } + + private bool TriggerWhereLevel(TriggerData t) + { + int minLevel, maxLevel; + if(!int.TryParse(t.Match.Groups[1].Value, out minLevel) || + !int.TryParse(t.Match.Groups[2].Value, out maxLevel)) + return false; + + Room cur = GetRoom(CurrentRoomId); + if(cur != null) + { + cur.Area.MinLevel = minLevel; + cur.Area.MaxLevel = maxLevel; + } + return false; + } + + private bool TriggerWhereName(TriggerData t) + { + string aName = Colors.RemoveColors(t.Match.Groups[1].Value, false).Trim(); + Room cur = GetRoom(CurrentRoomId); + if(cur != null) + cur.Area.Name = aName; + return false; + } + + private bool TriggerJoinedGQ(TriggerData t) + { + HasGQ = true; + return false; + } + + private bool TriggerLeftGQ(TriggerData t) + { + HasGQ = false; + return false; + } + + private bool TriggerCharStatusLevel(TriggerData t) + { + int i; + if(int.TryParse(t.Match.Groups[1].Value, out i)) + Level = i; + return false; + } + + private bool TriggerCharBaseTier(TriggerData t) + { + int i; + if(int.TryParse(t.Match.Groups[1].Value, out i)) + Tier = i; + return false; + } + + private bool TriggerCharBaseRemorts(TriggerData t) + { + int i; + if(int.TryParse(t.Match.Groups[1].Value, out i)) + Remorts = i; + return false; + } + + private bool TriggerRoomInfoNum(TriggerData t) + { + RoomInfoPrevious = RoomInfoEntry; + RoomInfoListen = true; + RoomInfoExits.Clear(); + uint i; + if(!uint.TryParse(t.Match.Groups[1].Value, out i)) + { + RoomInfoEntry = uint.MaxValue; + return false; + } + + RoomInfoEntry = i; + return false; + } + + private bool TriggerRoomInfoName(TriggerData t) + { + RoomInfoName = Colors.RemoveColors(t.Match.Groups[1].Value, true).Trim(); + return false; + } + + private bool TriggerRoomInfoArea(TriggerData t) + { + RoomInfoArea = t.Match.Groups[1].Value.Trim(); + return false; + } + + private bool TriggerRoomInfoExits(TriggerData t) + { + uint exitId; + RoomInfoExits[t.Match.Groups[1].Value.ToLower()[0]] = !uint.TryParse(t.Match.Groups[2].Value, out exitId) + ? uint.MaxValue + : exitId; + return false; + } + + private bool TriggerRoomInfoFinish(TriggerData t) + { + if(!RoomInfoListen) + return false; + + RoomInfoListen = false; + UpdateRoom(); + return false; + } + + #endregion + + #region Data + internal Dictionary IAreas = new Dictionary(); + internal Dictionary IRooms = new Dictionary(); + internal Dictionary IExits = new Dictionary(); + internal Dictionary IPortals = new Dictionary(); + + /// + /// Collection of all areas. + /// + public IEnumerable Areas + { + get + { + return IAreas.Values; + } + } + + /// + /// Collection of all rooms. + /// + public IEnumerable Rooms + { + get + { + return IRooms.Values; + } + } + + /// + /// Collection of all exits. + /// + public IEnumerable Exits + { + get + { + return IExits.Values; + } + } + + /// + /// Collection of all portals. + /// + public IEnumerable Portals + { + get + { + return IPortals.Values; + } + } + + private uint _guidArea = 0; + private uint _guidExit = 0; + + private void UpdateRoom() + { + CurrentRoomId = RoomInfoEntry; + + if(RoomInfoEntry == uint.MaxValue) + { + if(CurrentRoomId != RoomInfoPrevious) + { + Room prev = GetRoom(RoomInfoPrevious); + if(prev != null) + { + GetScript(prev).OnLeaveRoom(prev, null); + GetScript(prev).OnLeaveArea(prev.Area, null); + } + } + return; + } + + Room r = null; + Area a = null; + if(!IRooms.ContainsKey(CurrentRoomId)) + { + r = new Room(CurrentRoomId); + IRooms[r.Entry] = r; + } + else + r = IRooms[CurrentRoomId]; + + a = GetArea(RoomInfoArea); + if(a == null) + { + a = new Area(++_guidArea); + IAreas[a.Entry] = a; + a.Keyword = RoomInfoArea; + } + + if(r.Area != a) + r.Area = a; + + r.Name = RoomInfoName; + bool u = false; + foreach(KeyValuePair e in RoomInfoExits) + { + UpdateRoom(e.Value); + if(e.Value == uint.MaxValue) + continue; + Exit prev = r.GetExit(e.Key); + if(prev != null && prev.ToRoom == e.Value) + continue; + if(prev != null) + r.exits.Remove(prev); + Exit newExit = new Exit(++_guidExit); + newExit.To = GetRoom(e.Value); + newExit.Command = e.Key.ToString(); + newExit.From = r; + r.exits.Add(newExit); + IExits[newExit.Entry] = newExit; + u = true; + } + + if(u) + r.UpdateExits(); + + if(CurrentRoomId != RoomInfoPrevious) + { + Room prev = GetRoom(RoomInfoPrevious); + if(prev != null) + { + GetScript(prev).OnLeaveRoom(prev, r); + GetScript(prev).OnLeaveArea(prev.Area, r.Area); + } + GetScript(r).OnEnterArea(r.Area, prev != null ? prev.Area : null); + GetScript(r).OnEnterRoom(r, prev); + } + } + + private void UpdateRoom(uint Entry) + { + if(Entry == uint.MaxValue) + return; + + Room r = GetRoom(Entry); + if(r == null) + { + r = new Room(Entry); + IRooms[r.Entry] = r; + r.Area = GetArea(uint.MaxValue); + } + } + + internal uint CurrentRoomId; + private uint RoomInfoPrevious = uint.MaxValue; + private uint RoomInfoEntry = uint.MaxValue; + private string RoomInfoName; + private string RoomInfoArea; + private bool RoomInfoListen; + private Dictionary RoomInfoExits = new Dictionary(); + + /// + /// Get room by ID. + /// + /// ID of the room to get. + /// + public Room GetRoom(uint Entry) + { + return IRooms.ContainsKey(Entry) ? IRooms[Entry] : null; + } + + /// + /// Get exit by ID. + /// + /// ID of the exit to get. + /// + public Exit GetExit(uint Entry) + { + return IExits.ContainsKey(Entry) ? IExits[Entry] : null; + } + + /// + /// Get area by ID. + /// + /// ID of the area to get. + /// + public Area GetArea(uint Entry) + { + return IAreas.ContainsKey(Entry) ? IAreas[Entry] : null; + } + + /// + /// Get area by keyword. + /// + /// Keyword of area to get. + /// + public Area GetArea(string Keyword) + { + foreach(KeyValuePair x in IAreas) + { + if(x.Value.Keyword == Keyword) + return x.Value; + } + return null; + } + #endregion + + public override void Shutdown() + { + base.Shutdown(); + + Save(DBFileName); + } + + /// + /// Use mapper to go to the end of pathresult. + /// + /// + public void Goto(PathfindResult pr) + { + string sw = PathfindResult.Speedwalk(pr.Path); + if(string.IsNullOrEmpty(sw)) + sw = ""; + + if(Config.GetInt32("Speedwalk.Echo", 0) != 0) + { + World.Instance.SendMessage("@w{mapperpath}" + sw, Config.GetUInt64("Speedwalk.Echo.AuthMask", ulong.MaxValue)); + } + else if(!string.IsNullOrEmpty(sw)) + { + string[] swp = sw.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string x in swp) + World.Instance.Execute(x, true); + } + else + World.Instance.SendMessage("@wTrying to execute an empty path. Using goto when you are already at target?"); + } + + #region Saving + private void Load() + { + StreamReader f; + try + { + f = new StreamReader(DBFileName); + } + catch + { + // No database exists or we aren't allowed to read it. Make a new database. + return; + } + + Area[] data; + try + { + DataContractSerializer x = new DataContractSerializer(typeof(Area[])); + data = x.ReadObject(f.BaseStream) as Area[]; + } + catch + { + f.Close(); + Log.Error("Failed loading mapper database! File corrupted?"); + return; + } + + f.Close(); + + if(data == null) + return; + + foreach(Area a in data) + { + if(a == null) + continue; + + IAreas[a.Entry] = a; + if(a.Entry > _guidArea && a.Entry != uint.MaxValue) + _guidArea = a.Entry; + foreach(Room r in a.rooms) + { + if(r == null) + continue; + r.Area = a; + IRooms[r.Entry] = r; + foreach(Exit e in r.exits) + { + if(e == null) + continue; + e.From = r; + IExits[e.Entry] = e; + if(e.Entry > _guidExit) + _guidExit = e.Entry; + } + r.UpdateExits(); + } + foreach(Exit p in a.Portals) + { + p.To = GetRoom(p.ToRoom); + IPortals[p.Entry] = p; + if(p.Entry > _guidExit) + _guidExit = p.Entry; + } + } + + foreach(KeyValuePair e in IExits) + { + if(IPortals.ContainsKey(e.Key)) + { + IPortals[e.Key].To.Area.Portals.Remove(IPortals[e.Key]); + IPortals.Remove(e.Key); + } + } + + List toDelete = new List(); + foreach(KeyValuePair e in IExits) + { + e.Value.To = IRooms.ContainsKey(e.Value.ToRoom) ? IRooms[e.Value.ToRoom] : null; + if(e.Value.To == null) + toDelete.Add(e.Value); + } + + foreach(Exit e in toDelete) + { + IExits.Remove(e.Entry); + e.From.exits.Remove(e); + } + + // Successfully loaded a database. Now make a backup because we have a working copy at the moment. + File.Delete(DBFileBackup); + File.Copy(DBFileName, DBFileBackup); + } + + private void Save(string fileName) + { + if(IAreas.Count == 0) + return; + StreamWriter f = new StreamWriter(fileName, false); + + try + { + DataContractSerializer x = new DataContractSerializer(typeof(Area[])); + x.WriteObject(f.BaseStream, IAreas.Values.ToArray()); + } + catch(Exception e) + { + f.Close(); + throw e; + } + + f.Close(); + if(Config.GetInt32("AutoSave", 0) != 0) + WhenSave = World.Instance.MSTime + Config.GetInt32("AutoSave", 0) * 1000; + } + #endregion + + public override void Update(long msTime) + { + base.Update(msTime); + + if(WhenSave == 0 && Config.GetInt32("AutoSave", 0) != 0) + WhenSave = Config.GetInt32("AutoSave", 0) * 1000 + msTime; + else if(WhenSave > 0 && WhenSave <= msTime) + Save(DBFileName); + } + } + + public class MapperConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Tags.Exits", 1, "Display extra information about exits using {exits} tag. You need to have \"tags exits on\"."); + CreateSetting("Tags.Exits.Recon", 0, "Display exits that lead to rooms that haven't been reconed yet in dark red."); + CreateSetting("Speedwalk.Echo", 0, "Echo speedwalk to clients instead of executing the command when using goto. This is useful if you like your MUD client to process it instead of mapper just sending to MUD, for example portal alias etc."); + CreateSetting("Speedwalk.Echo.AuthMask", ulong.MaxValue, "Security mask of who to echo the commands if you set Echo to 1."); + CreateSetting("AutoSave", 0, "Save mapper database every X seconds. For example enter 600 to save mapper database every 10 minutes. Enter 0 to disable this feature. The map is also saved on shutdown of program. You can also type \"map save\" to manually save the database."); + } + } +} diff --git a/Mapper/Mapper.csproj b/Mapper/Mapper.csproj new file mode 100644 index 0000000..d7aaeb2 --- /dev/null +++ b/Mapper/Mapper.csproj @@ -0,0 +1,104 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D} + Library + Properties + Mapper + Mapper + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.0 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/Mapper/Pathfind.cs b/Mapper/Pathfind.cs new file mode 100644 index 0000000..1a81593 --- /dev/null +++ b/Mapper/Pathfind.cs @@ -0,0 +1,451 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Mapper.Scripting; + +namespace Mapper +{ + public partial class Mapper + { + private List[] _pGrid = new List[4096]; + private uint _pGridMax = 0; + private List[] _pExits = new[] { new List(), new List() }; + + private void Path_Init() + { + for(int i = 0; i < _pGrid.Length; i++) + _pGrid[i] = new List(512); + } + + private bool CanUsePortal(Exit p, Pathfinder x) + { + if(!p.HasFlag("recallmechanic")) + { + if(!x.CanUsePortals) + return false; + } + else + { + if(!x.CanUseRecalls) + return false; + } + + if(p.MinLevel > x.CharacterLevel + x.CharacterTier * 10 || + p.MaxLevel < x.CharacterLevel) + return false; + + if(p.HasFlag("nogq") && x.IsGlobalQuest) + return false; + + if(x.SkipPortals != null && x.SkipPortals.Contains(p)) + return false; + + return true; + } + + internal static AreaScript GetScript(Room r) + { + return AreaScriptMgr.GetScript(r.Area.Entry); + } + + public PathfindResult Get(Pathfinder p) + { + if(p.StartRooms != null) + p.StartRooms = p.StartRooms.Where(val => val != null).ToArray(); + if(p.StartRooms == null || p.StartRooms.Length == 0) + return new PathfindResult() { Success = false }; + + for(int i = 0; i < _pGridMax; i++) + _pGrid[i].Clear(); + _pExits[0].Clear(); + _pExits[1].Clear(); + _pGridMax = 0; + foreach(KeyValuePair x in IRooms) + { + x.Value.Mapper_OpenBy = null; + x.Value.Mapper_OpenCost = 0; + } + + p.OnStartedPathfind(); + + if(p.OverridePortals == null) + { + foreach(KeyValuePair x in IPortals) + { + if(!x.Value.HasFlag("disabled") && CanUsePortal(x.Value, p)) + _pExits[!x.Value.HasFlag("recallmechanic") ? 0 : 1].Add(x.Value); + } + } + else + { + foreach(Exit x in p.OverridePortals) + { + if(!x.HasFlag("disabled") && CanUsePortal(x, p)) + _pExits[!x.HasFlag("recallmechanic") ? 0 : 1].Add(x); + } + } + + Room Finish = null; + if(p.StartRooms != null) + { + foreach(Room r in p.StartRooms) + { + r.Mapper_OpenCost = uint.MaxValue; + if(OpenRoom(r, null, 0, p)) + { + Finish = r; + break; + } + } + } + + int itr1 = 0, itr2 = 0; + if(Finish == null) + { + while(itr1 < _pGrid.Length && itr1 < _pGridMax) + { + if(itr2 >= _pGrid[itr1].Count) + { + itr2 = 0; + itr1++; + continue; + } + + if(OpenRoom(_pGrid[itr1][itr2].To, _pGrid[itr1][itr2], (uint)itr1, p)) + { + Finish = _pGrid[itr1][itr2].To; + break; + } + + itr2++; + } + } + + PathfindResult pr = new PathfindResult(); + pr.Success = Finish != null; + pr.Cost = itr1; + pr.Target = Finish; + while(Finish != null && Finish.Mapper_OpenBy != null) + { + pr.Path.Insert(0, Finish.Mapper_OpenBy); + Finish = Finish.Mapper_OpenBy.From; + } + pr.Start = pr.Path.Count != 0 ? pr.Path[0].From : Finish; + + p.OnEndedPathFind(pr); + return pr; + } + + private bool OpenRoom(Room r, Exit from, uint cost, Pathfinder p) + { + if(r.Mapper_OpenBy != null || (from != null && p.StartRooms.Contains(r)) || r.Area.Entry == uint.MaxValue) + return false; + + r.Mapper_OpenBy = from; + r.Mapper_OpenCost = cost; + + if(p.IsTargetRoom(r)) + return true; + + if(_pExits[0].Count > 0 && !r.HasFlag("prison")) + { + for(int i = _pExits[0].Count - 1; i >= 0; i--) + { + if(_pExits[0][i].To.Mapper_OpenBy != null) + _pExits[0].RemoveAt(i); + else if(GetScript(r).CanUsePortal(_pExits[0][i], r) && p.CanUsePortal(_pExits[0][i], r)) + { + _pExits[0][i].From = r; + AddExit(_pExits[0][i], p, true); + _pExits[0].RemoveAt(i); + } + } + } + if(_pExits[1].Count > 0 && !r.HasFlag("norecall")) + { + for(int i = _pExits[1].Count - 1; i >= 0; i--) + { + if(_pExits[1][i].To.Mapper_OpenBy != null) + _pExits[1].RemoveAt(i); + else if(GetScript(r).CanUsePortal(_pExits[1][i], r) && p.CanUsePortal(_pExits[1][i], r)) + { + _pExits[1][i].From = r; + AddExit(_pExits[1][i], p, true); + _pExits[1].RemoveAt(i); + } + } + } + foreach(Exit e in r.exits) + { + if(e.To.Mapper_OpenBy != null) + continue; + if(e.To.Area.Entry == uint.MaxValue) + continue; + if(e.HasFlag("disabled")) + continue; + if(!GetScript(r).CanUseExit(e)) + continue; + if(e.MinLevel > p.CharacterLevel) + continue; + if(e.MaxLevel < p.CharacterLevel) + continue; + if(!p.CanUseExit(e)) + continue; + AddExit(e, p, false); + } + + return false; + } + + private void AddExit(Exit e, Pathfinder p, bool isPortal) + { + uint cost = e.From.Mapper_OpenCost + GetScript(e.From).GetLeaveRoomCost(e.From, e) + + (!isPortal ? GetScript(e.From).GetExitCost(e) : GetScript(e.From).GetPortalCost(e)) + GetScript(e.To).GetEnterRoomCost(e.To, e) + + p.GetLeaveRoomCost(e.From, e) + + (!isPortal ? p.GetExitCost(e) : p.GetPortalCost(e)) + p.GetEnterRoomCost(e.To, e); + + uint o = p.GetOverrideCost(e, isPortal); + if(o != uint.MaxValue) + cost = e.From.Mapper_OpenCost + o; + + if(cost >= _pGrid.Length) + return; + _pGrid[cost].Add(e); + cost++; + if(cost > _pGridMax) + _pGridMax = cost; + } + } + + public class PathfindResult + { + internal PathfindResult() + { + } + + public List Path = new List(); + public int Cost = 0; + public bool Success = false; + public Room Target; + public Room Start; + + /// + /// Generate a speedwalk from exit list. + /// + /// List of exits to generate a speedwalk from. + /// + public static string Speedwalk(IEnumerable Exits) + { + List sPath = new List(); + + foreach(Exit x in Exits) + { + string Door = Mapper.GetScript(x.From).GetDoorCommand(x); + string Command = Mapper.GetScript(x.From).GetExitCommand(x); + if(!string.IsNullOrEmpty(Door)) + sPath.AddRange(Door.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); + if(Command == null) + break; + sPath.AddRange(Command.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries)); + } + + StringBuilder strSW = new StringBuilder(); + StringBuilder strCur = new StringBuilder(); + foreach(string x in sPath) + { + if(IsDirectionCommand(x) != 'x') + { + strCur.Append(char.ToLower(x[0])); + } + else + { + if(strCur.Length > 0) + { + if(strSW.Length > 0) + strSW.Append(';'); + if(strCur.Length > 1) + strSW.Append("run "); + strSW.Append(CompressPath(strCur.ToString())); + strCur.Remove(0, strCur.Length); + } + + if(strSW.Length > 0) + strSW.Append(';'); + + strSW.Append(x); + } + } + + if(strCur.Length > 0) + { + if(strSW.Length > 0) + strSW.Append(';'); + if(strCur.Length > 1) + strSW.Append("run "); + strSW.Append(CompressPath(strCur.ToString())); + strCur.Remove(0, strCur.Length); + } + + return strSW.ToString(); + } + + /// + /// Check if command is direction command. For example "west" -> 'w'. If it's not a direction + /// then return 'x'. + /// + /// Command to check. + /// + public static char IsDirectionCommand(string cmd) + { + cmd = cmd.ToLower().Trim(); + + if(cmd.Length == 0) + return 'x'; + + if(cmd.Length <= "north".Length && "north".StartsWith(cmd)) + return 'n'; + if(cmd.Length <= "west".Length && "west".StartsWith(cmd)) + return 'w'; + if(cmd.Length <= "south".Length && "south".StartsWith(cmd)) + return 's'; + if(cmd.Length <= "east".Length && "east".StartsWith(cmd)) + return 'e'; + if(cmd.Length <= "down".Length && "down".StartsWith(cmd)) + return 'd'; + if(cmd.Length <= "up".Length && "up".StartsWith(cmd)) + return 'u'; + return 'x'; + } + + /// + /// Compresses "wwssse" into "2w3se". + /// + /// Path to compress. + /// + public static string CompressPath(string path) + { + char dir = 'x'; + int c = 0; + StringBuilder str = new StringBuilder(); + for(int i = 0; i < path.Length; i++) + { + if(path[i] == dir) + { + c++; + continue; + } + + if(dir == 'x') + { + dir = path[i]; + c = 1; + continue; + } + + if(c > 1) + str.Append(c.ToString()); + str.Append(dir.ToString()); + dir = path[i]; + c = 1; + } + + if(dir != 'x') + { + if(c > 1) + str.Append(c.ToString()); + str.Append(dir.ToString()); + } + + return str.ToString(); + } + } + + public class Pathfinder + { + protected Pathfinder() + { + } + + /// + /// Set the character level for this path find. It is used in level lock areas and portals. + /// + public int CharacterLevel = 1; + public int CharacterTier = 0; + public bool IsGlobalQuest = false; + public bool CanUsePortals = true; + public bool CanUseRecalls = true; + public bool IsSingleClassTier0 = false; + public Room[] SkipRooms; + public Exit[] SkipExits; + public Exit[] SkipPortals; + public Room[] StartRooms; + public Exit[] OverridePortals; + + public virtual void CopyFrom(Pathfinder pf) + { + if(pf == null) + return; + + CharacterLevel = pf.CharacterLevel; + CharacterTier = pf.CharacterTier; + IsGlobalQuest = pf.IsGlobalQuest; + CanUsePortals = pf.CanUsePortals; + CanUseRecalls = pf.CanUseRecalls; + IsSingleClassTier0 = pf.IsSingleClassTier0; + SkipRooms = pf.SkipRooms; + SkipExits = pf.SkipExits; + SkipPortals = pf.SkipPortals; + StartRooms = pf.StartRooms; + OverridePortals = pf.OverridePortals; + } + + public virtual uint GetOverrideCost(Exit e, bool isPortal) + { + return uint.MaxValue; + } + + public virtual void OnStartedPathfind() + { + } + + public virtual bool IsTargetRoom(Room r) + { + return false; + } + + public virtual void OnEndedPathFind(PathfindResult pr) + { + } + + public virtual bool CanUseExit(Exit e) + { + return true; + } + + public virtual bool CanUsePortal(Exit p, Room r) + { + return true; + } + + public virtual uint GetLeaveRoomCost(Room r, Exit e) + { + return 0; + } + + public virtual uint GetExitCost(Exit e) + { + return 0; + } + + public virtual uint GetPortalCost(Exit e) + { + return 0; + } + + public virtual uint GetEnterRoomCost(Room r, Exit e) + { + return 0; + } + } +} diff --git a/Mapper/Properties/.svn/all-wcprops b/Mapper/Properties/.svn/all-wcprops new file mode 100644 index 0000000..649af27 --- /dev/null +++ b/Mapper/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 39 +/svn/!svn/ver/2/trunk/Mapper/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/!svn/ver/2/trunk/Mapper/Properties/AssemblyInfo.cs +END diff --git a/Mapper/Properties/.svn/desktop.ini b/Mapper/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Properties/.svn/dir-prop-base b/Mapper/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/Mapper/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/Mapper/Properties/.svn/entries b/Mapper/Properties/.svn/entries new file mode 100644 index 0000000..c624b3e --- /dev/null +++ b/Mapper/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/Mapper/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:43.056138Z +7fb7e0921ad5edd499e2807ccac6b203 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1424 + diff --git a/Mapper/Properties/.svn/prop-base/desktop.ini b/Mapper/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Properties/.svn/props/desktop.ini b/Mapper/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/Mapper/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..d3fc0d8 --- /dev/null +++ b/Mapper/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Mapper")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Mapper")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("0cd0c5f1-be94-4ef7-bec4-a2f9ac9f7bed")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Mapper/Properties/.svn/text-base/desktop.ini b/Mapper/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Properties/.svn/tmp/desktop.ini b/Mapper/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Properties/.svn/tmp/prop-base/desktop.ini b/Mapper/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Properties/.svn/tmp/props/desktop.ini b/Mapper/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Properties/.svn/tmp/text-base/desktop.ini b/Mapper/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Properties/AssemblyInfo.cs b/Mapper/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d3fc0d8 --- /dev/null +++ b/Mapper/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Mapper")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Mapper")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("0cd0c5f1-be94-4ef7-bec4-a2f9ac9f7bed")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Mapper/Properties/desktop.ini b/Mapper/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Room.cs b/Mapper/Room.cs new file mode 100644 index 0000000..fd98160 --- /dev/null +++ b/Mapper/Room.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.Serialization; + +namespace Mapper +{ + [DataContract] + public class Room + { + public Room(uint entry) + { + Entry = entry; + } + + [DataMember] + public readonly uint Entry; + + [DataMember] + public string Name + { + get; + internal set; + } + + [DataMember] + public string Sector + { + get; + internal set; + } + + [DataMember] + internal List IFlags = null; + + public IEnumerable Flags + { + get + { + return IFlags; + } + } + + /// + /// Check if room has a flag. These are actual flags that you get from recon and not custom flags. + /// + /// Flag to check for. + /// + public bool HasFlag(string flag) + { + return IFlags != null && IFlags.Contains(flag.ToLower().Trim()); + } + + /// + /// Add a flag to room. These are actual flags that you get from recon and not custom flags. + /// + /// Flag to add. + public void AddFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(IFlags == null) + IFlags = new List(); + if(!string.IsNullOrEmpty(flag) && !IFlags.Contains(flag)) + IFlags.Add(flag); + } + + /// + /// Remove a flag from room. These are actual flags that you get from recon and not custom flags. + /// + /// Flag to remove. + /// + public bool RemoveFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(IFlags != null && IFlags.Contains(flag)) + { + IFlags.Remove(flag); + return true; + } + return false; + } + + [DataMember] + internal List CFlags = null; + + public IEnumerable CustomFlags + { + get + { + return CFlags; + } + } + + [DataMember] + public uint EntryCost + { + get; + internal set; + } + + [DataMember] + public uint LeaveCost + { + get; + internal set; + } + + /// + /// Check if room has a flag. These are custom flags and not actual flags like norecall which you get from recon. + /// + /// Flag to check for. + /// + public bool HasCustomFlag(string flag) + { + return CFlags != null && CFlags.Contains(flag.ToLower().Trim()); + } + + /// + /// Add a flag to room. These are custom flags and not actual flags like norecall which you get from recon. + /// + /// Flag to add. + public void AddCustomFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(CFlags == null) + CFlags = new List(); + if(!string.IsNullOrEmpty(flag) && !CFlags.Contains(flag)) + CFlags.Add(flag); + } + + /// + /// Remove a flag from room. These are custom flags and not actual flags like norecall which you get from recon. + /// + /// Flag to remove. + /// + public bool RemoveCustomFlag(string flag) + { + flag = flag != null ? flag.ToLower().Trim() : ""; + if(CFlags != null && CFlags.Contains(flag)) + { + CFlags.Remove(flag); + return true; + } + return false; + } + + [DataMember] + public int HealRate + { + get; + internal set; + } + + [DataMember] + public int ManaRate + { + get; + internal set; + } + + [DataMember] + internal List exits = new List(); + + internal uint Mapper_OpenCost = 0; + internal Exit Mapper_OpenBy = null; + + public Exit[] Exits + { + get + { + return exits.ToArray(); + } + } + + private Area _area; + + public Area Area + { + get + { + return _area; + } + internal set + { + if(_area != null) + _area.rooms.Remove(this); + _area = value; + if(!_area.rooms.Contains(this)) + _area.rooms.Add(this); + } + } + + public Exit GetExit(char Direction) + { + foreach(Exit e in exits) + { + if(!string.IsNullOrEmpty(e.Command) && e.Command.Length == 1 && e.Command[0] == Direction) + return e; + } + + return null; + } + + public Exit GetExit(string Command) + { + foreach(Exit e in exits) + { + if(!string.IsNullOrEmpty(e.Command) && e.Command == Command) + return e; + } + + return null; + } + + public Exit GetExit(uint Entry) + { + foreach(Exit e in exits) + { + if(e.Entry == Entry) + return e; + } + + return null; + } + + internal void UpdateExits() + { + SortedDictionary> x = new SortedDictionary>(); + foreach(Exit e in exits) + { + string cmd; + if(string.IsNullOrEmpty(e.Command) || string.IsNullOrEmpty(cmd = e.Command.ToLower().Trim())) + continue; + + e.Command = cmd; + if(!x.ContainsKey(e.Command)) + x[e.Command] = new List(); + x[e.Command].Add(e); + } + + exits.Clear(); + + foreach(KeyValuePair> i in x) + { + foreach(Exit j in i.Value) + exits.Add(j); + } + } + + public override string ToString() + { + return "Room '" + (Name ?? "NULL") + "' [" + Entry + "]"; + } + } +} diff --git a/Mapper/Scripting/.svn/all-wcprops b/Mapper/Scripting/.svn/all-wcprops new file mode 100644 index 0000000..4a8b129 --- /dev/null +++ b/Mapper/Scripting/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 39 +/svn/!svn/ver/38/trunk/Mapper/Scripting +END +AreaScript.cs +K 25 +svn:wc:ra_dav:version-url +V 53 +/svn/!svn/ver/38/trunk/Mapper/Scripting/AreaScript.cs +END diff --git a/Mapper/Scripting/.svn/desktop.ini b/Mapper/Scripting/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Scripting/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Scripting/.svn/dir-prop-base b/Mapper/Scripting/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/Mapper/Scripting/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/Mapper/Scripting/.svn/entries b/Mapper/Scripting/.svn/entries new file mode 100644 index 0000000..4659b19 --- /dev/null +++ b/Mapper/Scripting/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/Mapper/Scripting +http://proxymud.googlecode.com/svn + + + +2012-01-24T08:55:03.660104Z +38 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AreaScript.cs +file + + + + +2016-03-25T22:18:43.032138Z +d94fa1d66f2e719fb836c6d5b2448295 +2012-01-24T08:55:03.660104Z +38 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2509 + diff --git a/Mapper/Scripting/.svn/prop-base/desktop.ini b/Mapper/Scripting/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Scripting/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Scripting/.svn/props/desktop.ini b/Mapper/Scripting/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Scripting/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Scripting/.svn/text-base/AreaScript.cs.svn-base b/Mapper/Scripting/.svn/text-base/AreaScript.cs.svn-base new file mode 100644 index 0000000..54530fd --- /dev/null +++ b/Mapper/Scripting/.svn/text-base/AreaScript.cs.svn-base @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.IO; +using ProxyCore; + +namespace Mapper.Scripting +{ + public class AreaScript + { + public AreaScript(params uint[] AreaID) + { + AreaId = AreaID; + } + + public readonly uint[] AreaId; + + public int RequiredMapperVersion + { + get; + protected set; + } + + public virtual bool CanUseExit(Exit e) + { + return true; + } + + public virtual bool CanUsePortal(Exit p, Room r) + { + return true; + } + + public virtual string GetExitCommand(Exit e) + { + return e.Command == "stop" ? null : e.Command; + } + + public virtual string GetDoorCommand(Exit e) + { + if(!string.IsNullOrEmpty(e.DoorCommand)) + return e.DoorCommand; + if(e.HasFlag("door") && PathfindResult.IsDirectionCommand(e.Command) != 'x') + return "open " + e.Command; + return string.Empty; + } + + public virtual uint GetLeaveRoomCost(Room r, Exit e) + { + return r.LeaveCost; + } + + public virtual uint GetExitCost(Exit e) + { + return e.Cost; + } + + public virtual uint GetPortalCost(Exit e) + { + return e.Cost; + } + + public virtual uint GetEnterRoomCost(Room r, Exit e) + { + return r.EntryCost; + } + + public virtual void OnEnterRoom(Room r, Room oldRoom) + { + } + + public virtual void OnLeaveRoom(Room r, Room newRoom) + { + } + + public virtual void OnEnterArea(Area a, Area oldArea) + { + } + + public virtual void OnLeaveArea(Area a, Area newArea) + { + } + } + + public class AreaScriptMgr + { + internal static Dictionary Scripts = new Dictionary(); + internal static AreaScript Default = new AreaScript(); + + public static void RegisterScript(uint AreaId, AreaScript Script) + { + if(Script != null) + Scripts[AreaId] = Script; + } + + internal static AreaScript GetScript(uint AreaId) + { + return Scripts.ContainsKey(AreaId) ? Scripts[AreaId] : Default; + } + } +} diff --git a/Mapper/Scripting/.svn/text-base/desktop.ini b/Mapper/Scripting/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Scripting/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Scripting/.svn/tmp/desktop.ini b/Mapper/Scripting/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Scripting/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Scripting/.svn/tmp/prop-base/desktop.ini b/Mapper/Scripting/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Scripting/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Scripting/.svn/tmp/props/desktop.ini b/Mapper/Scripting/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Scripting/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Scripting/.svn/tmp/text-base/desktop.ini b/Mapper/Scripting/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Scripting/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/Scripting/AreaScript.cs b/Mapper/Scripting/AreaScript.cs new file mode 100644 index 0000000..54530fd --- /dev/null +++ b/Mapper/Scripting/AreaScript.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.IO; +using ProxyCore; + +namespace Mapper.Scripting +{ + public class AreaScript + { + public AreaScript(params uint[] AreaID) + { + AreaId = AreaID; + } + + public readonly uint[] AreaId; + + public int RequiredMapperVersion + { + get; + protected set; + } + + public virtual bool CanUseExit(Exit e) + { + return true; + } + + public virtual bool CanUsePortal(Exit p, Room r) + { + return true; + } + + public virtual string GetExitCommand(Exit e) + { + return e.Command == "stop" ? null : e.Command; + } + + public virtual string GetDoorCommand(Exit e) + { + if(!string.IsNullOrEmpty(e.DoorCommand)) + return e.DoorCommand; + if(e.HasFlag("door") && PathfindResult.IsDirectionCommand(e.Command) != 'x') + return "open " + e.Command; + return string.Empty; + } + + public virtual uint GetLeaveRoomCost(Room r, Exit e) + { + return r.LeaveCost; + } + + public virtual uint GetExitCost(Exit e) + { + return e.Cost; + } + + public virtual uint GetPortalCost(Exit e) + { + return e.Cost; + } + + public virtual uint GetEnterRoomCost(Room r, Exit e) + { + return r.EntryCost; + } + + public virtual void OnEnterRoom(Room r, Room oldRoom) + { + } + + public virtual void OnLeaveRoom(Room r, Room newRoom) + { + } + + public virtual void OnEnterArea(Area a, Area oldArea) + { + } + + public virtual void OnLeaveArea(Area a, Area newArea) + { + } + } + + public class AreaScriptMgr + { + internal static Dictionary Scripts = new Dictionary(); + internal static AreaScript Default = new AreaScript(); + + public static void RegisterScript(uint AreaId, AreaScript Script) + { + if(Script != null) + Scripts[AreaId] = Script; + } + + internal static AreaScript GetScript(uint AreaId) + { + return Scripts.ContainsKey(AreaId) ? Scripts[AreaId] : Default; + } + } +} diff --git a/Mapper/Scripting/desktop.ini b/Mapper/Scripting/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/Scripting/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Mapper/desktop.ini b/Mapper/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Mapper/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/.svn/all-wcprops b/MobDB/.svn/all-wcprops new file mode 100644 index 0000000..ef91fd8 --- /dev/null +++ b/MobDB/.svn/all-wcprops @@ -0,0 +1,23 @@ +K 25 +svn:wc:ra_dav:version-url +V 28 +/svn/!svn/ver/57/trunk/MobDB +END +Mob.cs +K 25 +svn:wc:ra_dav:version-url +V 35 +/svn/!svn/ver/44/trunk/MobDB/Mob.cs +END +MobDB.csproj +K 25 +svn:wc:ra_dav:version-url +V 41 +/svn/!svn/ver/30/trunk/MobDB/MobDB.csproj +END +MobDB.cs +K 25 +svn:wc:ra_dav:version-url +V 37 +/svn/!svn/ver/57/trunk/MobDB/MobDB.cs +END diff --git a/MobDB/.svn/desktop.ini b/MobDB/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/.svn/dir-prop-base b/MobDB/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/MobDB/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/MobDB/.svn/entries b/MobDB/.svn/entries new file mode 100644 index 0000000..d2dd633 --- /dev/null +++ b/MobDB/.svn/entries @@ -0,0 +1,133 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/MobDB +http://proxymud.googlecode.com/svn + + + +2012-02-08T08:41:07.654540Z +57 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +MobDB.cs +file + + + + +2016-03-25T22:18:43.020138Z +9a5bdafe30e90ab584e89a5b306172db +2012-02-08T08:41:07.654540Z +57 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +47799 + +Properties +dir + +Mob.cs +file + + + + +2016-03-25T22:18:43.020138Z +bd0098124bb23de92ed01bf42623b33e +2012-01-29T09:57:36.978045Z +44 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +5728 + +MobDB.csproj +file + + + + +2016-03-25T22:18:43.020138Z +37f046a6d787788f20284b560615e701 +2012-01-23T07:49:14.623991Z +30 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2879 + diff --git a/MobDB/.svn/prop-base/desktop.ini b/MobDB/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/.svn/props/desktop.ini b/MobDB/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/.svn/text-base/Mob.cs.svn-base b/MobDB/.svn/text-base/Mob.cs.svn-base new file mode 100644 index 0000000..5206373 --- /dev/null +++ b/MobDB/.svn/text-base/Mob.cs.svn-base @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; + +namespace MobDB +{ + [DataContract] + public class Mob + { + internal Mob(uint e) + { + Entry = e; + } + + /// + /// ID of the mob. + /// + [DataMember] + public readonly uint Entry; + + /// + /// (Short)name of the mob, such as "an ant". This is what we see when fighting the mob. + /// This field is an array because same longname mobs can sometimes have multiple different names. + /// Names here are automatically normalized where first letter of the name is made lower case, rest + /// is left intact. Keep that in mind when comparing names. + /// + public string[] Name + { + get + { + return _name; + } + internal set + { + _name = value ?? new string[0]; + for(int i = 0; i < _name.Length; i++) + { + if(!string.IsNullOrEmpty(_name[i])) + _name[i] = MobDB.NormalizeName(_name[i]); + } + } + } + + [DataMember] + private string[] _name; + + /// + /// Return all names separated by a comma ",". + /// + public string Names + { + get + { + string str = ""; + foreach(string x in Name) + { + if(str.Length > 0) + str += ", "; + str += x; + } + + return str; + } + } + + /// + /// Long name of the mob, this is what we see in room when we type look. + /// + [DataMember] + public string Longname + { + get; + internal set; + } + + /// + /// Keywords of the mob, can be multiple separated with a space. + /// + [DataMember] + public string Keyword + { + get; + internal set; + } + + /// + /// Level of the mob, we get this from lastkills. For non-killable mobs this is -1. + /// + [DataMember] + public int Level + { + get; + internal set; + } + + /// + /// Keywords of areas this mob can be in. Have a keyword "all" if you want mob to be in any area like the Firestorm Phoenix. + /// + [DataMember] + public string[] Areas + { + get; + internal set; + } + + /// + /// Override the default color of mob with this. Set to null or empty string to disable this and use + /// default color from the configuration file instead. + /// + [DataMember] + public string DefaultColor + { + get; + internal set; + } + + [DataMember] + internal List Flags; + + [DataMember] + internal List Locations = new List(); + + /// + /// Calculate chance that mob will be in room with this ID. + /// + /// Room ID to check mob in. + /// + public double GetChance(uint RoomId) + { + foreach(MobLocation ml in Locations) + { + if(ml.RoomEntry != RoomId) + continue; + + return (ml.CountSeen * 100) / (double)(ml.CountSeen - ml.TimesSeen + ml.TimesVisited); + } + + return 0.0; + } + + /// + /// Calculate room ID with best chance of having mob in it. + /// + /// + public uint GetBestRoom() + { + double b = 0.0; + uint c = uint.MaxValue; + foreach(MobLocation ml in Locations) + { + double a = GetChance(ml.RoomEntry); + if(a > b) + { + b = a; + c = ml.RoomEntry; + } + } + + return c; + } + + /// + /// Add a flag to mob. + /// + /// Flag to add. + public void AddFlag(string f) + { + if(f == null) + return; + f = f.ToLower().Trim(); + if(f.Length == 0) + return; + + if(Flags == null) + Flags = new List(); + if(!Flags.Contains(f)) + Flags.Add(f); + } + + /// + /// Remove a flag from mob. + /// + /// Flag to remove. + /// + public bool RemoveFlag(string f) + { + if(f == null || Flags == null) + return false; + f = f.ToLower().Trim(); + if(f.Length == 0) + return false; + return Flags.Remove(f); + } + + public bool HasFlag(string f) + { + if(f == null) + return false; + f = f.ToLower().Trim(); + if(f.Length == 0) + return false; + return Flags != null && Flags.Contains(f); + } + } +} diff --git a/MobDB/.svn/text-base/MobDB.cs.svn-base b/MobDB/.svn/text-base/MobDB.cs.svn-base new file mode 100644 index 0000000..5a1889f --- /dev/null +++ b/MobDB/.svn/text-base/MobDB.cs.svn-base @@ -0,0 +1,1210 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using ProxyCore; +using ProxyCore.Input; +using ProxyCore.Output; +using ProxyCore.Scripting; + +namespace MobDB +{ + public class MobDB : Plugin + { + public MobDB() + : base("mobdb", "Mob database") + { + Author = "Duckbat"; + Version = 5; + Description = + "You can add mobs to database so we can automatically record their locations and later find them when campaigning."; + UpdateUrl = "www.duckbat.com/plugins/update.mobdb.txt"; + Website = "code.google.com/p/proxymud/"; + + RequiredPlayerConfig.Add("tags roomchars on"); + + Config = new MobDBConfig(); + + RegisterCommand("mobdb", "", CommandMobDB); + RegisterCommand("add", @"(.+)", CommandAdd, 0, CMDFlags.None, "mobdb"); + RegisterCommand("count", "", CommandCount, 0, CMDFlags.None, "mobdb"); + RegisterCommand("delete", @"(.+)", CommandDelete, 0, CMDFlags.None, "mobdb"); + RegisterCommand("find", @"(exact\s+)?(case\s+)?(.+)", CommandFind, 0, CMDFlags.None, "mobdb"); + RegisterCommand("mobinfo", @"^\s*(\d+)?(\s+.+)?", CommandMobInfo, 3, CMDFlags.None, "mobdb"); + RegisterCommand("save", @"(.+)", CommandSave, 0, CMDFlags.None, "mobdb"); + RegisterCommand("where", @"(.+)", CommandWhere, 2, CMDFlags.None, "mobdb"); + + RegisterTrigger("room.id", @"^\$gmcp\.room\.info\.num (-?\d+)$", TriggerRoomInfoNum); + RegisterTrigger("room.area", @"^\$gmcp\.room\.info\.zone (.*)$", TriggerRoomInfoArea); + RegisterTrigger("room.chars1", @"^@w\{roomchars\}$", TriggerRoomChars1, TriggerFlags.None, 1500); + RegisterTrigger("room.chars2", @"^@w\{/roomchars\}$", TriggerRoomChars2, TriggerFlags.None, 500); + RegisterTrigger("room.chars3", @"(.*)", TriggerRoomChars3); + RegisterTrigger("lastkills1", "@WName Level Exp From", TriggerLastKills, + TriggerFlags.NotRegex); + RegisterTrigger("lastkills2", @"^@w(.{30})\s+(\d+)\s+\d+\s+(.+)", TriggerLastKills2); + + Load(); + } + + private string AddMobName; + private int AddMobLevel; + private string AddMobKeywords; + private string AddMobArea; + + private uint CurrentTime; + private uint RoomInfoEntry = uint.MaxValue; + private string RoomInfoArea; + private const string DBFileName = "mobdb.xml"; + private const string DBFileBackup = "mobdb_backup.xml"; + private readonly Dictionary IMobs = new Dictionary(); + private uint _guidMob = 0; + private long WhenSave = 0; + private readonly List UnknownMobs = new List(); + private bool ListeningRoomChars = false; + private readonly Dictionary overrideColors = new Dictionary(); + private int ChooseFromUnknown = 0; + private readonly Dictionary> RoomLocations = new Dictionary>(); + + /// + /// A collection of all mobs. + /// + public IEnumerable Mobs + { + get + { + return IMobs.Values; + } + } + + /// + /// Normalize mob name. This means first character of name will be made lower case while others are left as is. + /// + /// Name of mob. + /// + public static string NormalizeName(string mobName) + { + if(mobName == null) + return string.Empty; + + if(mobName.Length > 0) + mobName = mobName.Substring(0, 1).ToLower() + mobName.Substring(1); + return mobName; + } + + /// + /// Override mob colors when replacing with our longname. + /// + /// Mob ID. + /// Color code, for example "@M" + public void SetMobColor(uint Entry, string colorCode) + { + if(string.IsNullOrEmpty(colorCode)) + overrideColors.Remove(Entry); + else + overrideColors[Entry] = colorCode; + } + + /// + /// Clear all overridden mob colors. + /// + public void ClearMobColors() + { + overrideColors.Clear(); + } + + /// + /// Get mob by ID. + /// + /// ID of mob. + /// + public Mob GetMob(uint Id) + { + return IMobs.ContainsKey(Id) ? IMobs[Id] : null; + } + + private Mob GetMob(ref string t) + { + string orig = t; + t = t.Trim(); + + // Mobs can't be afk + if(t.StartsWith("@w[AFK]")) + { + t = orig; + return null; + } + + StringBuilder str = new StringBuilder(); + + // Unit is wounded + if(t.StartsWith("@R(Wounded)")) + { + t = Colors.RemoveDuplicateColors("@R" + t.Substring("@R(Wounded)".Length)).Trim(); + str.Append("@R(Wounded)"); + } + + if(t.StartsWith("@w(Invis)")) + { + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(Invis)".Length)).Trim(); + str.Append("@w(Invis) "); + } + else if(t.StartsWith("@w(I)")) + { + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(I)".Length)).Trim(); + str.Append("@w(I)"); + } + + if(t.StartsWith("@w(Hidden)")) + { + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(Hidden)".Length)).Trim(); + str.Append("@w(Hidden) "); + } + else if(t.StartsWith("@w(H)")) + { + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(H)".Length)).Trim(); + str.Append("@w(H)"); + } + + if(t.StartsWith("@W(Translucent)")) + { + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(Translucent)".Length)).Trim(); + str.Append("@W(Translucent) "); + } + else if(t.StartsWith("@W(T)")) + { + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(T)".Length)).Trim(); + str.Append("@W(T)"); + } + + if(t.StartsWith("@C(Charmed) ")) + { + //t = Colors.RemoveDuplicateColors("@C" + t.Substring("@C(Charmed) ".Length)); + t = orig; + return null; + } + else if(t.StartsWith("@C(C)")) + { + //t = Colors.RemoveDuplicateColors("@C" + t.Substring("@C(C)".Length)); + t = orig; + return null; + } + + if(t.StartsWith("@M(Animated) ")) + { + //t = Colors.RemoveDuplicateColors("@M" + t.Substring("@M(Animated) ".Length)); + t = orig; + return null; + } + else if(t.StartsWith("@M(A)")) + { + //t = Colors.RemoveDuplicateColors("@M" + t.Substring("@M(A)".Length)); + t = orig; + return null; + } + + if(t.StartsWith("@B(Diseased)")) + { + t = Colors.RemoveDuplicateColors("@B" + t.Substring("@B(Diseased)".Length)).Trim(); + str.Append("@B(Diseased) "); + } + else if(t.StartsWith("@B(D)")) + { + t = Colors.RemoveDuplicateColors("@B" + t.Substring("@B(D)".Length)).Trim(); + str.Append("@B(D)"); + } + + if(t.StartsWith("@D(Marked)")) + { + t = Colors.RemoveDuplicateColors("@D" + t.Substring("@D(Marked)".Length)).Trim(); + str.Append("@D(Marked) "); + } + else if(t.StartsWith("@D(X)")) + { + t = Colors.RemoveDuplicateColors("@D" + t.Substring("@D(X)".Length)).Trim(); + str.Append("@D(X)"); + } + + if(t.StartsWith("@r(Red Aura)")) + { + t = Colors.RemoveDuplicateColors("@r" + t.Substring("@r(Red Aura)".Length)).Trim(); + str.Append("@r(Red Aura)"); + } + else if(t.StartsWith("@r(R)")) + { + t = Colors.RemoveDuplicateColors("@r" + t.Substring("@r(R)".Length)).Trim(); + str.Append("@r(R)"); + } + else if(t.StartsWith("@y(Golden Aura)")) + { + t = Colors.RemoveDuplicateColors("@y" + t.Substring("@y(Golden Aura)".Length)).Trim(); + str.Append("@y(Golden Aura) "); + } + else if(t.StartsWith("@y(G)")) + { + t = Colors.RemoveDuplicateColors("@y" + t.Substring("@y(G)".Length)).Trim(); + str.Append("@y(G)"); + } + + if(t.StartsWith("@W(White Aura)")) + { + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(White Aura)".Length)).Trim(); + str.Append("@W(White Aura) "); + } + else if(t.StartsWith("@W(W)")) + { + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(W)".Length)).Trim(); + str.Append("@W(W)"); + } + + if(t.StartsWith("@R(Angry)")) + { + t = Colors.RemoveDuplicateColors("@R" + t.Substring("@R(Angry)".Length)).Trim(); + str.Append("@R(Angry) "); + } + + if(t.StartsWith("@w(Linkdead)")) + { + t = orig; + return null; + } + + if(t.StartsWith("@R(RAIDER)")) + { + t = orig; + return null; + } + + if(t.StartsWith("@R(TRAITOR)")) + { + t = orig; + return null; + } + + if(t.StartsWith("@G(DEFENDER)")) + { + t = orig; + return null; + } + + if(t.StartsWith("@R(WANTED)")) + { + t = orig; + return null; + } + + if(str.Length != 0 && str[str.Length - 1] != ' ') + str.Append(' '); + str.Append("{MOB}"); + + if(t.EndsWith("[TARGET]")) + { + t = Colors.RemoveDuplicateColors(t.Substring(0, t.LastIndexOf("[TARGET]"))).Trim(); + str.Append(" @R[TARGET]"); + } + + string area = RoomInfoArea.ToLower(); + foreach(KeyValuePair x in IMobs) + { + if(x.Value.Areas == null) + continue; + + if(!x.Value.Areas.Contains("all") && !x.Value.Areas.Contains(RoomInfoArea)) + continue; + + if(x.Value.Longname == t) + { + string n = Config.GetString("Mob.Longname", + "@w[@G$mob.level@w] $mob.color$mob.name @D($mob.keywords)"); + if(string.IsNullOrEmpty(n)) + { + t = orig; + return x.Value; + } + + n = n.Replace("$$", "\nE"); + n = n.Replace("$mob.level", x.Value.Level.ToString()); + n = n.Replace("$mob.keywords", x.Value.Keyword); + if(n.Contains("$mob.name")) + n = n.Replace("$mob.name", x.Value.Names); + n = n.Replace("$mob.color", + overrideColors.ContainsKey(x.Key) + ? overrideColors[x.Key] + : (!string.IsNullOrEmpty(x.Value.DefaultColor) + ? x.Value.DefaultColor + : Config.GetString("Mob.DefaultColor", "@y"))); + n = n.Replace("$mob.longname", x.Value.Longname); + n = n.Replace("$mob.entry", x.Key.ToString()); + n = n.Replace("\nE", "$"); + str.Replace("{MOB}", Colors.RemoveDuplicateColors(n)); + t = str.ToString(); + return x.Value; + } + } + + if(!UnknownMobs.Contains(t)) + UnknownMobs.Add(t); + t = orig; + return null; + } + + public override void OnEnteredCommandBefore(ref string Msg, uint ClientId, int AuthLevel) + { + base.OnEnteredCommandBefore(ref Msg, ClientId, AuthLevel); + + if(ChooseFromUnknown == 2) + { + int i; + if(int.TryParse(Msg, out i) && i >= 1 && i <= UnknownMobs.Count) + { + Msg = null; + i--; + ChooseFromUnknown = 0; + + Mob m = new Mob(++_guidMob); + m.Name = new[] { AddMobName }; + m.Longname = UnknownMobs[i]; + UnknownMobs.RemoveAt(i); + m.Level = AddMobLevel; + m.Keyword = AddMobKeywords; + m.Areas = new[] {AddMobArea}; + IMobs[m.Entry] = m; + + if(RoomInfoEntry != uint.MaxValue) + { + MobLocation ml = new MobLocation(); + ml.CountSeen = 1; + ml.LastVisited = 0; + ml.MobEntry = m.Entry; + ml.RoomEntry = RoomInfoEntry; + ml.TimesSeen = 1; + ml.TimesVisited = 1; + m.Locations.Add(ml); + + if(!RoomLocations.ContainsKey(RoomInfoEntry)) + RoomLocations[RoomInfoEntry] = new List(); + RoomLocations[RoomInfoEntry].Add(ml); + } + + World.Instance.SendMessage("@wAdded a new mob to database '@G" + m.Names + " @w' [@Y" + m.Entry + + "@w] and set location to current room."); + } + } + } + + #region Commands + private bool CommandWhere(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: mobdb where ", i.ClientMask); + World.Instance.SendMessage("@wShows up to @W20 @wbest locations for a mob.", i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage(" @wmobdb where ", i.ClientMask); + World.Instance.SendMessage("@wShows up to @W5 @wbest locations for all mobs that match this name in current area.", i.ClientMask); + return true; + } + + uint mobId; + if(uint.TryParse(i.Arguments.Groups[1].Value, out mobId)) + { + Mob m = GetMob(mobId); + if(m == null) + { + World.Instance.SendMessage("@wNo such mob in database (@R" + mobId + "@w).", i.ClientMask); + return true; + } + + if(!ShowLocations(m, 20, i.ClientMask, false)) + World.Instance.SendMessage("@wDon't know where '@G" + m.Names + "@w' [@Y" + m.Entry + "@w] is.", i.ClientMask); + } + else + { + string n = i.Arguments.Groups[1].Value.ToLower(); + bool showed = false; + foreach(KeyValuePair x in IMobs) + { + if(!x.Value.Areas.Contains("all") && !x.Value.Areas.Contains(RoomInfoArea)) + continue; + + bool f = false; + foreach(string z in x.Value.Name) + { + if(!z.ToLower().Contains(n)) + continue; + + f = true; + break; + } + + if(!f) + continue; + + showed = ShowLocations(x.Value, 5, i.ClientMask, showed); + } + + if(!showed) + World.Instance.SendMessage("@wFound no mob locations with that name.", i.ClientMask); + } + + return true; + } + + private bool ShowLocations(Mob m, int count, uint[] cm, bool isMulti) + { + SortedDictionary> bl = new SortedDictionary>(); + foreach(MobLocation ml in m.Locations) + { + double c = m.GetChance(ml.RoomEntry); + if(!bl.ContainsKey(c)) + bl[c] = new List(); + bl[c].Add(ml.RoomEntry); + } + + StringBuilder strRooms = new StringBuilder(); + bool showed = false; + IEnumerable>> x = bl.Reverse(); + foreach(KeyValuePair> y in x) + { + foreach(uint z in y.Value) + { + if(!showed) + { + if(isMulti) + World.Instance.SendMessage("", cm); + World.Instance.SendMessage("@wLocations for '@G" + m.Names + "@w' [@Y" + m.Entry + "@w]:", cm); + showed = true; + } + + World.Instance.SendMessage("@Y" + string.Format("{0,-6}", z) + " @C" + string.Format("{0:0.00}", y.Key) + "%", cm); + if(strRooms.Length > 0) + strRooms.Append(' '); + strRooms.Append(z); + count--; + if(count == 0) + break; + } + + if(count == 0) + break; + } + + if(strRooms.Length > 0) + World.Instance.SendMessage("@wmap goto " + strRooms); + + return showed; + } + + private bool CommandMobDB(InputData i) + { + World.Instance.SendMessage("@wAvailable mob db commands:", i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb add") + " @w- Add a new mob to database.", i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb count") + " @w- Shows how many mobs you have in database.", + i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb delete") + " @w- Delete a mob from database.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "mobdb find") + " @w- Find mobs in database.", + i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb mobinfo") + " @w- Show information or edit a mob.", i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb save") + " @w- Save mob database to file.", i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb where") + " @w- Show mob locations.", i.ClientMask); + return true; + } + + private bool CommandAdd(InputData i) + { + string n; + if(!i.Arguments.Success || (n = i.Arguments.Groups[1].Value.Trim()).Length == 0) + { + World.Instance.SendMessage("@wSyntax: mobdb add ", i.ClientMask); + return true; + } + + if(UnknownMobs.Count == 0) + { + World.Instance.SendMessage("@wWe have no mobs with unknown longname in roomlist.", i.ClientMask); + return true; + } + + AddMobKeywords = n; + ChooseFromUnknown = 1; + World.Instance.Execute("lastkills 1", false); + return true; + } + + private bool CommandMobInfo(InputData i) + { + if(!i.Arguments.Success || i.Arguments.Groups[1].Length == 0) + { + World.Instance.SendMessage("@wSyntax: mobdb mobinfo [option] [value]", i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage("@wAvailable options for mob:", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "addflag") + " @w- Add a flag to mob.", + i.ClientMask); + World.Instance.SendMessage( + "@W" + string.Format("{0,-15}", "removeflag") + " @w- Remove a flag from mob.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "name1") + " @w- Change (short)name of mob.", + i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "name2") + " @w- Change second (short)name of mob.", + i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "name3") + " @w- Change third (short)name of mob. (This goes all the way up to 999).", + i.ClientMask); + World.Instance.SendMessage( + "@W" + string.Format("{0,-15}", "areas") + + " @w- Set allowed areas for mob. Separate with space, enter all to allow all areas.", i.ClientMask); + World.Instance.SendMessage( + "@W" + string.Format("{0,-15}", "color") + " @w- Change default color of mob name. Enter default as value to set default color from config.", i.ClientMask); + World.Instance.SendMessage( + "@W" + string.Format("{0,-15}", "keywords") + " @w- Change keywords for mob.", i.ClientMask); + World.Instance.SendMessage( + "@W" + string.Format("{0,-15}", "longname") + " @w- Change longname (roomname) of mob.", + i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "level") + " @w- Change level of mob.", + i.ClientMask); + World.Instance.SendMessage("@wEnter '@Wclear@w' as a shortname to remove that name from mob.", i.ClientMask); + return true; + } + + uint mobId; + if(!uint.TryParse(i.Arguments.Groups[1].Value, out mobId)) + { + World.Instance.SendMessage("@wInvalid mob ID given (@R" + i.Arguments.Groups[1].Value + "@w).", + i.ClientMask); + return true; + } + + Mob r = GetMob(mobId); + if(r == null) + { + World.Instance.SendMessage("@wNo such mob in database (@R" + mobId + "@w).", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[2].Length != 0) + { + string key, val; + string str = i.Arguments.Groups[2].Value.Trim(); + if(str.Contains(' ')) + { + key = str.Substring(0, str.IndexOf(' ')).ToLower(); + val = str.Substring(key.Length).Trim(); + + switch(key) + { + case "addflag": + r.AddFlag(val); + break; + + case "removeflag": + r.RemoveFlag(val); + break; + + case "name": + if(r.Name.Length == 0) + r.Name = new[] { val }; + else + r.Name[0] = val; + break; + + case "level": + { + int lvl; + if(int.TryParse(val, out lvl)) + r.Level = lvl; + } break; + + case "areas": + { + string[] spl = val.ToLower().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if(spl.Length > 0) + r.Areas = spl; + } break; + + case "keywords": + { + if(val.Length > 0) + r.Keyword = val; + } break; + + case "longname": + { + if(val == "clear") + r.Longname = ""; + else + r.Longname = val; + } break; + + case "color": + { + if(val == "clear" || val == "default" || val == "none") + r.DefaultColor = null; + else if(val.Length > 0) + r.DefaultColor = val; + } break; + } + + if(key.StartsWith("name") && key.Length > 4) + { + key = key.Substring(4); + int nth; + if(int.TryParse(key, out nth) && nth >= 1) + { + nth--; + List Names = r.Name.ToList(); + if(nth >= r.Name.Length) + { + if(val != "clear") + Names.Add(val); + } + else if(val != "clear") + Names[nth] = val; + else + Names.RemoveAt(nth); + + r.Name = Names.ToArray(); + } + } + } + } + + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", + i.ClientMask); + World.Instance.SendMessage("@w| @WEntry @w: @Y" + string.Format("{0,-55}", r.Entry) + "@w|", + i.ClientMask); + int k = 0; + foreach(string z in r.Name) + { + ++k; + string tag = "Name (" + k + ")"; + World.Instance.SendMessage( + "@w| @W" + string.Format("{0,-11}", tag) + " @w: @G" + + string.Format("{0,-55}", !string.IsNullOrEmpty(z) ? z : "Unknown") + "@w|", i.ClientMask); + } + string area = ""; + if(r.Areas != null) + { + foreach(string y in r.Areas) + { + if(area.Length > 0) + area += " "; + area += y; + } + } + World.Instance.SendMessage("@w| @WArea @w: @M" + string.Format("{0,-55}", area) + "@w|", i.ClientMask); + { + StringBuilder strFlags = new StringBuilder(); + if(r.Flags != null) + { + foreach(string x in r.Flags) + { + if(strFlags.Length > 0) + strFlags.Append(", "); + strFlags.Append(x); + } + } + + string[] spl = Utility.WrapColored(strFlags.ToString(), 54, 0); + for(int j = 0; j < spl.Length; j++) + { + if(j == 0) + World.Instance.SendMessage("@w| @WFlags @w: " + string.Format("{0,-55}", spl[j]) + "@w|", + i.ClientMask); + else + World.Instance.SendMessage("@w| : " + string.Format("{0,-55}", spl[j]) + "@w|", + i.ClientMask); + } + } + + World.Instance.SendMessage("@w| @WLongname @w: " + Utility.FormatColoredString(r.Longname, -55) + "@w|", + i.ClientMask); + World.Instance.SendMessage("@w| @WKeywords @w: " + string.Format("{0,-55}", r.Keyword) + "@w|", + i.ClientMask); + World.Instance.SendMessage("@w| @WLevel @w: @Y" + string.Format("{0,-55}", r.Level) + "@w|", + i.ClientMask); + + string defColor = Config.GetString("Mob.DefaultColor", "@y"); + if(string.IsNullOrEmpty(r.DefaultColor)) + defColor += "Default"; + else + defColor = r.DefaultColor + r.DefaultColor.Replace("@", "@@"); + World.Instance.SendMessage("@w| @WColor @w: " + Utility.FormatColoredString(defColor, -55) + "@w|", + i.ClientMask); + + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", + i.ClientMask); + return true; + } + + private bool CommandFind(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: mobdb find [exact] [case] ", i.ClientMask); + return true; + } + + string name = i.Arguments.Groups[3].Value; + if(i.Arguments.Groups[2].Length == 0) + name = name.ToLower(); + else if(name.Length >= 1) + name = NormalizeName(name); + + World.Instance.SendMessage("@wSearched for '@W" + name + "@w'.", i.ClientMask); + List Found = new List(); + foreach(KeyValuePair x in IMobs) + { + if(i.Arguments.Groups[1].Length != 0) + { + if(i.Arguments.Groups[2].Length != 0) + { + if(x.Value.Name.Contains(name)) + Found.Add(x.Value); + } + else + { + foreach(string z in x.Value.Name) + { + if(z.ToLower() == name) + { + Found.Add(x.Value); + break; + } + } + } + } + else + { + if(i.Arguments.Groups[2].Length != 0) + { + if(x.Value.Name.Contains(name)) + Found.Add(x.Value); + } + else + { + foreach(string z in x.Value.Name) + { + if(z.ToLower().Contains(name)) + { + Found.Add(x.Value); + break; + } + } + } + } + } + + if(Found.Count != 0) + { + World.Instance.SendMessage("@WEntry Name Area Room", i.ClientMask); + World.Instance.SendMessage("@G====== ============================================= ============ ======", + i.ClientMask); + foreach(Mob x in Found) + { + string area = ""; + if(x.Areas != null) + { + foreach(string y in x.Areas) + { + if(area.Length != 0) + area += " "; + area += y; + } + } + string room = ""; + uint ro = x.GetBestRoom(); + if(ro != uint.MaxValue) + room = ro.ToString(); + World.Instance.SendMessage("@Y" + string.Format("{0,-6}", x.Entry) + " @c" + + string.Format( + "{0,-" + "=============================================".Length + "}", + x.Names) + " @g" + string.Format("{0,-12}", area) + " @Y" + room); + } + } + + World.Instance.SendMessage("@wFound @C" + Found.Count + " @wmob" + (Found.Count != 1 ? "s" : "") + ".", + i.ClientMask); + return true; + } + + private bool CommandDelete(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: mobdb delete ", i.ClientMask); + World.Instance.SendMessage(" @wmobdb delete ", i.ClientMask); + World.Instance.SendMessage(" @wmobdb delete locations", i.ClientMask); + World.Instance.SendMessage(" @wmobdb delete all", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[1].Value.ToLower().Trim() == "locations") + { + foreach(KeyValuePair x in IMobs) + { + x.Value.Locations.Clear(); + } + + World.Instance.SendMessage("@wDeleted all mob locations.", i.ClientMask); + return true; + } + + uint id; + if(uint.TryParse(i.Arguments.Groups[1].Value, out id)) + { + if(!IMobs.ContainsKey(id)) + World.Instance.SendMessage("@wNo such mob in database (@R" + id + "@w).", i.ClientMask); + else + { + World.Instance.SendMessage("@wDeleted '@G" + IMobs[id].Names + "@w'.", i.ClientMask); + foreach(MobLocation ml in IMobs[id].Locations) + { + RoomLocations[ml.RoomEntry].Remove(ml); + if(RoomLocations[ml.RoomEntry].Count == 0) + RoomLocations.Remove(ml.RoomEntry); + } + IMobs.Remove(id); + } + } + else + { + string key = i.Arguments.Groups[1].Value.ToLower().Trim(); + bool isConfirm = false; + if(key.Contains(' ')) + { + isConfirm = key.Substring(key.IndexOf(' ') + 1).Trim() == "confirm"; + key = key.Substring(0, key.IndexOf(' ')); + } + + if(key == "all" && !isConfirm) + { + World.Instance.SendMessage("@wAre you sure you wish to delete all mobs? Enter '@Wmobdb delete all confirm@w' to confirm.", i.ClientMask); + return true; + } + + List del = new List(); + foreach(KeyValuePair x in IMobs) + { + if(key == "all" || x.Value.Areas.Contains(key)) + del.Add(x.Key); + } + + foreach(uint x in del) + IMobs.Remove(x); + + World.Instance.SendMessage("@wDeleted @C" + del.Count + " @wmob" + (del.Count != 1 ? "s" : "") + ".", i.ClientMask); + } + return true; + } + + private bool CommandCount(InputData i) + { + int thisArea = 0; + int total = IMobs.Count; + if(!string.IsNullOrEmpty(RoomInfoArea)) + { + foreach(KeyValuePair x in IMobs) + { + if(x.Value.Areas == null || x.Value.Areas.Length == 0) + continue; + if(x.Value.Areas.Contains(RoomInfoArea)) + thisArea++; + } + } + World.Instance.SendMessage("@wYou have added @G" + thisArea + " @wmobs to database in this area.", + i.ClientMask); + World.Instance.SendMessage("@wYou have added @G" + total + " @wmobs to database in all areas.", i.ClientMask); + return true; + } + + private bool CommandSave(InputData i) + { + string fileName = DBFileName; + if(i.Arguments.Success) + fileName = i.Arguments.Groups[0].Value; + + Save(fileName); + World.Instance.SendMessage("@wSaved mob database to '@W" + fileName + "@w'.", i.ClientMask); + return true; + } + + #endregion + + #region Triggers + + private bool TriggerLastKills(TriggerData t) + { + if(ChooseFromUnknown == 1) + ChooseFromUnknown = 2; + return false; + } + + private bool TriggerLastKills2(TriggerData t) + { + if(ChooseFromUnknown == 0) + return false; + + AddMobName = t.Match.Groups[1].Value.Trim(); + if(!int.TryParse(t.Match.Groups[2].Value, out AddMobLevel) || UnknownMobs.Count == 0) + { + ChooseFromUnknown = 0; + return false; + } + AddMobArea = RoomInfoArea; + + int i = 1; + World.Instance.SendMessage("@wSelect mob longname from list (type number):"); + foreach(string x in UnknownMobs) + { + World.Instance.SendMessage("@W" + i + ". " + x); + i++; + } + return false; + } + + private bool TriggerRoomChars1(TriggerData t) + { + foundMobs.Clear(); + UnknownMobs.Clear(); + ListeningRoomChars = true; + if(Config.GetInt32("Tags.Gag", 1) != 0) + t.Msg.AuthMask = 0; + + CurrentTime = (uint)(DateTime.UtcNow - new DateTime(2012, 1, 1)).TotalSeconds; + return false; + } + + private bool TriggerRoomChars2(TriggerData t) + { + ListeningRoomChars = false; + if(Config.GetInt32("Tags.Gag", 1) != 0) + t.Msg.AuthMask = 0; + if(RoomInfoEntry != uint.MaxValue && RoomLocations.ContainsKey(RoomInfoEntry)) + { + foreach(MobLocation x in RoomLocations[RoomInfoEntry]) + { + Mob m = GetMob(x.MobEntry); + if(m != null && CurrentTime - x.LastVisited >= 600) + { + x.TimesVisited++; + if(foundMobs.Contains(m)) + x.TimesSeen++; + x.LastVisited = CurrentTime; + } + } + } + return false; + } + + private List foundMobs = new List(); + private bool TriggerRoomChars3(TriggerData t) + { + if(!ListeningRoomChars) + return false; + + string n = t.Msg.Msg; + Mob m = GetMob(ref n); + t.Msg.Msg = n; + + if(m == null) + return false; + + if(RoomInfoEntry != uint.MaxValue) + { + if(!foundMobs.Contains(m)) + foundMobs.Add(m); + + bool f = false; + foreach(MobLocation x in m.Locations) + { + if(x.RoomEntry != RoomInfoEntry) + continue; + + f = true; + if(CurrentTime - x.LastVisited >= 600) + x.CountSeen++; + break; + } + + if(!f) + { + MobLocation ml = new MobLocation(); + ml.RoomEntry = RoomInfoEntry; + ml.MobEntry = m.Entry; + ml.LastVisited = CurrentTime; + ml.TimesSeen = 0; + ml.TimesVisited = 0; + ml.CountSeen = 1; + m.Locations.Add(ml); + if(!RoomLocations.ContainsKey(RoomInfoEntry)) + RoomLocations[RoomInfoEntry] = new List(); + RoomLocations[RoomInfoEntry].Add(ml); + } + } + return false; + } + + private bool TriggerRoomInfoNum(TriggerData t) + { + if(ChooseFromUnknown != 0) + { + ChooseFromUnknown = 0; + World.Instance.SendMessage("@wChanged room. Mob adding was cancelled."); + } + UnknownMobs.Clear(); + uint i; + if(!uint.TryParse(t.Match.Groups[1].Value, out i)) + { + RoomInfoEntry = uint.MaxValue; + return false; + } + + RoomInfoEntry = i; + return false; + } + + private bool TriggerRoomInfoArea(TriggerData t) + { + RoomInfoArea = t.Match.Groups[1].Value.Trim(); + return false; + } + + #endregion + + #region Saving & Loading + + public override void Shutdown() + { + base.Shutdown(); + + Save(DBFileName); + } + + private void Load() + { + StreamReader f; + try + { + f = new StreamReader(DBFileName); + } + catch + { + // No database exists or we aren't allowed to read it. Make a new database. + return; + } + + Mob[] data; + try + { + DataContractSerializer x = new DataContractSerializer(typeof(Mob[])); + data = x.ReadObject(f.BaseStream) as Mob[]; + } + catch + { + f.Close(); + Log.Error("Failed loading mob database! File corrupted?"); + return; + } + + f.Close(); + + if(data == null) + return; + + foreach(Mob a in data) + { + if(a == null) + continue; + + IMobs[a.Entry] = a; + if(a.Entry > _guidMob && a.Entry != uint.MaxValue) + _guidMob = a.Entry; + + foreach(MobLocation m in a.Locations) + { + if(!RoomLocations.ContainsKey(m.RoomEntry)) + RoomLocations[m.RoomEntry] = new List(); + RoomLocations[m.RoomEntry].Add(m); + } + } + + // Successfully loaded a database. Now make a backup because we have a working copy at the moment. + File.Delete(DBFileBackup); + File.Copy(DBFileName, DBFileBackup); + } + + private void Save(string fileName) + { + if(IMobs.Count == 0) + return; + StreamWriter f = new StreamWriter(fileName, false); + + try + { + DataContractSerializer x = new DataContractSerializer(typeof(Mob[])); + x.WriteObject(f.BaseStream, IMobs.Values.ToArray()); + } + catch(Exception e) + { + f.Close(); + throw e; + } + + f.Close(); + if(Config.GetInt32("AutoSave", 0) != 0) + WhenSave = World.Instance.MSTime + Config.GetInt32("AutoSave", 0) * 1000; + } + + #endregion + + public override void Update(long msTime) + { + base.Update(msTime); + + if(WhenSave == 0 && Config.GetInt32("AutoSave", 0) != 0) + WhenSave = Config.GetInt32("AutoSave", 0) * 1000 + msTime; + else if(WhenSave > 0 && WhenSave <= msTime) + Save(DBFileName); + } + } + + [DataContract] + internal class MobLocation + { + [DataMember] + internal uint MobEntry; + + [DataMember] + internal uint RoomEntry; + + [DataMember] + internal uint TimesSeen; + + [DataMember] + internal uint CountSeen; + + [DataMember] + internal uint TimesVisited; + + [DataMember] + internal uint LastVisited; + } + + public class MobDBConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Mob.DefaultColor", "@y", "Default color for the mob. Color can be changed by other plugins"); + CreateSetting("Mob.Longname", "@w[@G$mob.level@w] $mob.color$mob.name @D($mob.keywords)", "Replace mob room name with this format. Set this to \"\" to disable mob replacing. You can have variables in the name such as:\n$mob.entry - This is the unique ID of mob (our assigned not mud).\n$mob.level - Level of the mob.\n$mob.name - Mob shortname, what appears when you are fighting it.\n$mob.longname - Mob room name.\n$mob.keywords - These are mob keywords.\n$mob.color - This is the color of the mob name. Can be set from other plugins or manually changed.\nEnter $$ to escape the $ character."); + CreateSetting("AutoSave", 0, "Save mob database every X seconds. For example enter 600 to save mob database every 10 minutes. Enter 0 to disable this feature. The database is also saved on shutdown of program. You can also type \"mobdb save\" to manually save the database."); + CreateSetting("Tags.Gag", 1, "Gag {roomchars} tags for clients."); + } + } +} diff --git a/MobDB/.svn/text-base/MobDB.csproj.svn-base b/MobDB/.svn/text-base/MobDB.csproj.svn-base new file mode 100644 index 0000000..8d3480b --- /dev/null +++ b/MobDB/.svn/text-base/MobDB.csproj.svn-base @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {8A7FE055-E1B4-4967-9C76-CA46BA854E73} + Library + Properties + MobDB + MobDB + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.0 + + + 3.5 + + + 3.5 + + + + + + + + + + + + \ No newline at end of file diff --git a/MobDB/.svn/text-base/desktop.ini b/MobDB/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/.svn/tmp/desktop.ini b/MobDB/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/.svn/tmp/prop-base/desktop.ini b/MobDB/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/.svn/tmp/props/desktop.ini b/MobDB/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/.svn/tmp/text-base/desktop.ini b/MobDB/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/Mob.cs b/MobDB/Mob.cs new file mode 100644 index 0000000..5206373 --- /dev/null +++ b/MobDB/Mob.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; + +namespace MobDB +{ + [DataContract] + public class Mob + { + internal Mob(uint e) + { + Entry = e; + } + + /// + /// ID of the mob. + /// + [DataMember] + public readonly uint Entry; + + /// + /// (Short)name of the mob, such as "an ant". This is what we see when fighting the mob. + /// This field is an array because same longname mobs can sometimes have multiple different names. + /// Names here are automatically normalized where first letter of the name is made lower case, rest + /// is left intact. Keep that in mind when comparing names. + /// + public string[] Name + { + get + { + return _name; + } + internal set + { + _name = value ?? new string[0]; + for(int i = 0; i < _name.Length; i++) + { + if(!string.IsNullOrEmpty(_name[i])) + _name[i] = MobDB.NormalizeName(_name[i]); + } + } + } + + [DataMember] + private string[] _name; + + /// + /// Return all names separated by a comma ",". + /// + public string Names + { + get + { + string str = ""; + foreach(string x in Name) + { + if(str.Length > 0) + str += ", "; + str += x; + } + + return str; + } + } + + /// + /// Long name of the mob, this is what we see in room when we type look. + /// + [DataMember] + public string Longname + { + get; + internal set; + } + + /// + /// Keywords of the mob, can be multiple separated with a space. + /// + [DataMember] + public string Keyword + { + get; + internal set; + } + + /// + /// Level of the mob, we get this from lastkills. For non-killable mobs this is -1. + /// + [DataMember] + public int Level + { + get; + internal set; + } + + /// + /// Keywords of areas this mob can be in. Have a keyword "all" if you want mob to be in any area like the Firestorm Phoenix. + /// + [DataMember] + public string[] Areas + { + get; + internal set; + } + + /// + /// Override the default color of mob with this. Set to null or empty string to disable this and use + /// default color from the configuration file instead. + /// + [DataMember] + public string DefaultColor + { + get; + internal set; + } + + [DataMember] + internal List Flags; + + [DataMember] + internal List Locations = new List(); + + /// + /// Calculate chance that mob will be in room with this ID. + /// + /// Room ID to check mob in. + /// + public double GetChance(uint RoomId) + { + foreach(MobLocation ml in Locations) + { + if(ml.RoomEntry != RoomId) + continue; + + return (ml.CountSeen * 100) / (double)(ml.CountSeen - ml.TimesSeen + ml.TimesVisited); + } + + return 0.0; + } + + /// + /// Calculate room ID with best chance of having mob in it. + /// + /// + public uint GetBestRoom() + { + double b = 0.0; + uint c = uint.MaxValue; + foreach(MobLocation ml in Locations) + { + double a = GetChance(ml.RoomEntry); + if(a > b) + { + b = a; + c = ml.RoomEntry; + } + } + + return c; + } + + /// + /// Add a flag to mob. + /// + /// Flag to add. + public void AddFlag(string f) + { + if(f == null) + return; + f = f.ToLower().Trim(); + if(f.Length == 0) + return; + + if(Flags == null) + Flags = new List(); + if(!Flags.Contains(f)) + Flags.Add(f); + } + + /// + /// Remove a flag from mob. + /// + /// Flag to remove. + /// + public bool RemoveFlag(string f) + { + if(f == null || Flags == null) + return false; + f = f.ToLower().Trim(); + if(f.Length == 0) + return false; + return Flags.Remove(f); + } + + public bool HasFlag(string f) + { + if(f == null) + return false; + f = f.ToLower().Trim(); + if(f.Length == 0) + return false; + return Flags != null && Flags.Contains(f); + } + } +} diff --git a/MobDB/MobDB.cs b/MobDB/MobDB.cs new file mode 100644 index 0000000..5a1889f --- /dev/null +++ b/MobDB/MobDB.cs @@ -0,0 +1,1210 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using ProxyCore; +using ProxyCore.Input; +using ProxyCore.Output; +using ProxyCore.Scripting; + +namespace MobDB +{ + public class MobDB : Plugin + { + public MobDB() + : base("mobdb", "Mob database") + { + Author = "Duckbat"; + Version = 5; + Description = + "You can add mobs to database so we can automatically record their locations and later find them when campaigning."; + UpdateUrl = "www.duckbat.com/plugins/update.mobdb.txt"; + Website = "code.google.com/p/proxymud/"; + + RequiredPlayerConfig.Add("tags roomchars on"); + + Config = new MobDBConfig(); + + RegisterCommand("mobdb", "", CommandMobDB); + RegisterCommand("add", @"(.+)", CommandAdd, 0, CMDFlags.None, "mobdb"); + RegisterCommand("count", "", CommandCount, 0, CMDFlags.None, "mobdb"); + RegisterCommand("delete", @"(.+)", CommandDelete, 0, CMDFlags.None, "mobdb"); + RegisterCommand("find", @"(exact\s+)?(case\s+)?(.+)", CommandFind, 0, CMDFlags.None, "mobdb"); + RegisterCommand("mobinfo", @"^\s*(\d+)?(\s+.+)?", CommandMobInfo, 3, CMDFlags.None, "mobdb"); + RegisterCommand("save", @"(.+)", CommandSave, 0, CMDFlags.None, "mobdb"); + RegisterCommand("where", @"(.+)", CommandWhere, 2, CMDFlags.None, "mobdb"); + + RegisterTrigger("room.id", @"^\$gmcp\.room\.info\.num (-?\d+)$", TriggerRoomInfoNum); + RegisterTrigger("room.area", @"^\$gmcp\.room\.info\.zone (.*)$", TriggerRoomInfoArea); + RegisterTrigger("room.chars1", @"^@w\{roomchars\}$", TriggerRoomChars1, TriggerFlags.None, 1500); + RegisterTrigger("room.chars2", @"^@w\{/roomchars\}$", TriggerRoomChars2, TriggerFlags.None, 500); + RegisterTrigger("room.chars3", @"(.*)", TriggerRoomChars3); + RegisterTrigger("lastkills1", "@WName Level Exp From", TriggerLastKills, + TriggerFlags.NotRegex); + RegisterTrigger("lastkills2", @"^@w(.{30})\s+(\d+)\s+\d+\s+(.+)", TriggerLastKills2); + + Load(); + } + + private string AddMobName; + private int AddMobLevel; + private string AddMobKeywords; + private string AddMobArea; + + private uint CurrentTime; + private uint RoomInfoEntry = uint.MaxValue; + private string RoomInfoArea; + private const string DBFileName = "mobdb.xml"; + private const string DBFileBackup = "mobdb_backup.xml"; + private readonly Dictionary IMobs = new Dictionary(); + private uint _guidMob = 0; + private long WhenSave = 0; + private readonly List UnknownMobs = new List(); + private bool ListeningRoomChars = false; + private readonly Dictionary overrideColors = new Dictionary(); + private int ChooseFromUnknown = 0; + private readonly Dictionary> RoomLocations = new Dictionary>(); + + /// + /// A collection of all mobs. + /// + public IEnumerable Mobs + { + get + { + return IMobs.Values; + } + } + + /// + /// Normalize mob name. This means first character of name will be made lower case while others are left as is. + /// + /// Name of mob. + /// + public static string NormalizeName(string mobName) + { + if(mobName == null) + return string.Empty; + + if(mobName.Length > 0) + mobName = mobName.Substring(0, 1).ToLower() + mobName.Substring(1); + return mobName; + } + + /// + /// Override mob colors when replacing with our longname. + /// + /// Mob ID. + /// Color code, for example "@M" + public void SetMobColor(uint Entry, string colorCode) + { + if(string.IsNullOrEmpty(colorCode)) + overrideColors.Remove(Entry); + else + overrideColors[Entry] = colorCode; + } + + /// + /// Clear all overridden mob colors. + /// + public void ClearMobColors() + { + overrideColors.Clear(); + } + + /// + /// Get mob by ID. + /// + /// ID of mob. + /// + public Mob GetMob(uint Id) + { + return IMobs.ContainsKey(Id) ? IMobs[Id] : null; + } + + private Mob GetMob(ref string t) + { + string orig = t; + t = t.Trim(); + + // Mobs can't be afk + if(t.StartsWith("@w[AFK]")) + { + t = orig; + return null; + } + + StringBuilder str = new StringBuilder(); + + // Unit is wounded + if(t.StartsWith("@R(Wounded)")) + { + t = Colors.RemoveDuplicateColors("@R" + t.Substring("@R(Wounded)".Length)).Trim(); + str.Append("@R(Wounded)"); + } + + if(t.StartsWith("@w(Invis)")) + { + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(Invis)".Length)).Trim(); + str.Append("@w(Invis) "); + } + else if(t.StartsWith("@w(I)")) + { + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(I)".Length)).Trim(); + str.Append("@w(I)"); + } + + if(t.StartsWith("@w(Hidden)")) + { + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(Hidden)".Length)).Trim(); + str.Append("@w(Hidden) "); + } + else if(t.StartsWith("@w(H)")) + { + t = Colors.RemoveDuplicateColors("@w" + t.Substring("@w(H)".Length)).Trim(); + str.Append("@w(H)"); + } + + if(t.StartsWith("@W(Translucent)")) + { + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(Translucent)".Length)).Trim(); + str.Append("@W(Translucent) "); + } + else if(t.StartsWith("@W(T)")) + { + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(T)".Length)).Trim(); + str.Append("@W(T)"); + } + + if(t.StartsWith("@C(Charmed) ")) + { + //t = Colors.RemoveDuplicateColors("@C" + t.Substring("@C(Charmed) ".Length)); + t = orig; + return null; + } + else if(t.StartsWith("@C(C)")) + { + //t = Colors.RemoveDuplicateColors("@C" + t.Substring("@C(C)".Length)); + t = orig; + return null; + } + + if(t.StartsWith("@M(Animated) ")) + { + //t = Colors.RemoveDuplicateColors("@M" + t.Substring("@M(Animated) ".Length)); + t = orig; + return null; + } + else if(t.StartsWith("@M(A)")) + { + //t = Colors.RemoveDuplicateColors("@M" + t.Substring("@M(A)".Length)); + t = orig; + return null; + } + + if(t.StartsWith("@B(Diseased)")) + { + t = Colors.RemoveDuplicateColors("@B" + t.Substring("@B(Diseased)".Length)).Trim(); + str.Append("@B(Diseased) "); + } + else if(t.StartsWith("@B(D)")) + { + t = Colors.RemoveDuplicateColors("@B" + t.Substring("@B(D)".Length)).Trim(); + str.Append("@B(D)"); + } + + if(t.StartsWith("@D(Marked)")) + { + t = Colors.RemoveDuplicateColors("@D" + t.Substring("@D(Marked)".Length)).Trim(); + str.Append("@D(Marked) "); + } + else if(t.StartsWith("@D(X)")) + { + t = Colors.RemoveDuplicateColors("@D" + t.Substring("@D(X)".Length)).Trim(); + str.Append("@D(X)"); + } + + if(t.StartsWith("@r(Red Aura)")) + { + t = Colors.RemoveDuplicateColors("@r" + t.Substring("@r(Red Aura)".Length)).Trim(); + str.Append("@r(Red Aura)"); + } + else if(t.StartsWith("@r(R)")) + { + t = Colors.RemoveDuplicateColors("@r" + t.Substring("@r(R)".Length)).Trim(); + str.Append("@r(R)"); + } + else if(t.StartsWith("@y(Golden Aura)")) + { + t = Colors.RemoveDuplicateColors("@y" + t.Substring("@y(Golden Aura)".Length)).Trim(); + str.Append("@y(Golden Aura) "); + } + else if(t.StartsWith("@y(G)")) + { + t = Colors.RemoveDuplicateColors("@y" + t.Substring("@y(G)".Length)).Trim(); + str.Append("@y(G)"); + } + + if(t.StartsWith("@W(White Aura)")) + { + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(White Aura)".Length)).Trim(); + str.Append("@W(White Aura) "); + } + else if(t.StartsWith("@W(W)")) + { + t = Colors.RemoveDuplicateColors("@W" + t.Substring("@W(W)".Length)).Trim(); + str.Append("@W(W)"); + } + + if(t.StartsWith("@R(Angry)")) + { + t = Colors.RemoveDuplicateColors("@R" + t.Substring("@R(Angry)".Length)).Trim(); + str.Append("@R(Angry) "); + } + + if(t.StartsWith("@w(Linkdead)")) + { + t = orig; + return null; + } + + if(t.StartsWith("@R(RAIDER)")) + { + t = orig; + return null; + } + + if(t.StartsWith("@R(TRAITOR)")) + { + t = orig; + return null; + } + + if(t.StartsWith("@G(DEFENDER)")) + { + t = orig; + return null; + } + + if(t.StartsWith("@R(WANTED)")) + { + t = orig; + return null; + } + + if(str.Length != 0 && str[str.Length - 1] != ' ') + str.Append(' '); + str.Append("{MOB}"); + + if(t.EndsWith("[TARGET]")) + { + t = Colors.RemoveDuplicateColors(t.Substring(0, t.LastIndexOf("[TARGET]"))).Trim(); + str.Append(" @R[TARGET]"); + } + + string area = RoomInfoArea.ToLower(); + foreach(KeyValuePair x in IMobs) + { + if(x.Value.Areas == null) + continue; + + if(!x.Value.Areas.Contains("all") && !x.Value.Areas.Contains(RoomInfoArea)) + continue; + + if(x.Value.Longname == t) + { + string n = Config.GetString("Mob.Longname", + "@w[@G$mob.level@w] $mob.color$mob.name @D($mob.keywords)"); + if(string.IsNullOrEmpty(n)) + { + t = orig; + return x.Value; + } + + n = n.Replace("$$", "\nE"); + n = n.Replace("$mob.level", x.Value.Level.ToString()); + n = n.Replace("$mob.keywords", x.Value.Keyword); + if(n.Contains("$mob.name")) + n = n.Replace("$mob.name", x.Value.Names); + n = n.Replace("$mob.color", + overrideColors.ContainsKey(x.Key) + ? overrideColors[x.Key] + : (!string.IsNullOrEmpty(x.Value.DefaultColor) + ? x.Value.DefaultColor + : Config.GetString("Mob.DefaultColor", "@y"))); + n = n.Replace("$mob.longname", x.Value.Longname); + n = n.Replace("$mob.entry", x.Key.ToString()); + n = n.Replace("\nE", "$"); + str.Replace("{MOB}", Colors.RemoveDuplicateColors(n)); + t = str.ToString(); + return x.Value; + } + } + + if(!UnknownMobs.Contains(t)) + UnknownMobs.Add(t); + t = orig; + return null; + } + + public override void OnEnteredCommandBefore(ref string Msg, uint ClientId, int AuthLevel) + { + base.OnEnteredCommandBefore(ref Msg, ClientId, AuthLevel); + + if(ChooseFromUnknown == 2) + { + int i; + if(int.TryParse(Msg, out i) && i >= 1 && i <= UnknownMobs.Count) + { + Msg = null; + i--; + ChooseFromUnknown = 0; + + Mob m = new Mob(++_guidMob); + m.Name = new[] { AddMobName }; + m.Longname = UnknownMobs[i]; + UnknownMobs.RemoveAt(i); + m.Level = AddMobLevel; + m.Keyword = AddMobKeywords; + m.Areas = new[] {AddMobArea}; + IMobs[m.Entry] = m; + + if(RoomInfoEntry != uint.MaxValue) + { + MobLocation ml = new MobLocation(); + ml.CountSeen = 1; + ml.LastVisited = 0; + ml.MobEntry = m.Entry; + ml.RoomEntry = RoomInfoEntry; + ml.TimesSeen = 1; + ml.TimesVisited = 1; + m.Locations.Add(ml); + + if(!RoomLocations.ContainsKey(RoomInfoEntry)) + RoomLocations[RoomInfoEntry] = new List(); + RoomLocations[RoomInfoEntry].Add(ml); + } + + World.Instance.SendMessage("@wAdded a new mob to database '@G" + m.Names + " @w' [@Y" + m.Entry + + "@w] and set location to current room."); + } + } + } + + #region Commands + private bool CommandWhere(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: mobdb where ", i.ClientMask); + World.Instance.SendMessage("@wShows up to @W20 @wbest locations for a mob.", i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage(" @wmobdb where ", i.ClientMask); + World.Instance.SendMessage("@wShows up to @W5 @wbest locations for all mobs that match this name in current area.", i.ClientMask); + return true; + } + + uint mobId; + if(uint.TryParse(i.Arguments.Groups[1].Value, out mobId)) + { + Mob m = GetMob(mobId); + if(m == null) + { + World.Instance.SendMessage("@wNo such mob in database (@R" + mobId + "@w).", i.ClientMask); + return true; + } + + if(!ShowLocations(m, 20, i.ClientMask, false)) + World.Instance.SendMessage("@wDon't know where '@G" + m.Names + "@w' [@Y" + m.Entry + "@w] is.", i.ClientMask); + } + else + { + string n = i.Arguments.Groups[1].Value.ToLower(); + bool showed = false; + foreach(KeyValuePair x in IMobs) + { + if(!x.Value.Areas.Contains("all") && !x.Value.Areas.Contains(RoomInfoArea)) + continue; + + bool f = false; + foreach(string z in x.Value.Name) + { + if(!z.ToLower().Contains(n)) + continue; + + f = true; + break; + } + + if(!f) + continue; + + showed = ShowLocations(x.Value, 5, i.ClientMask, showed); + } + + if(!showed) + World.Instance.SendMessage("@wFound no mob locations with that name.", i.ClientMask); + } + + return true; + } + + private bool ShowLocations(Mob m, int count, uint[] cm, bool isMulti) + { + SortedDictionary> bl = new SortedDictionary>(); + foreach(MobLocation ml in m.Locations) + { + double c = m.GetChance(ml.RoomEntry); + if(!bl.ContainsKey(c)) + bl[c] = new List(); + bl[c].Add(ml.RoomEntry); + } + + StringBuilder strRooms = new StringBuilder(); + bool showed = false; + IEnumerable>> x = bl.Reverse(); + foreach(KeyValuePair> y in x) + { + foreach(uint z in y.Value) + { + if(!showed) + { + if(isMulti) + World.Instance.SendMessage("", cm); + World.Instance.SendMessage("@wLocations for '@G" + m.Names + "@w' [@Y" + m.Entry + "@w]:", cm); + showed = true; + } + + World.Instance.SendMessage("@Y" + string.Format("{0,-6}", z) + " @C" + string.Format("{0:0.00}", y.Key) + "%", cm); + if(strRooms.Length > 0) + strRooms.Append(' '); + strRooms.Append(z); + count--; + if(count == 0) + break; + } + + if(count == 0) + break; + } + + if(strRooms.Length > 0) + World.Instance.SendMessage("@wmap goto " + strRooms); + + return showed; + } + + private bool CommandMobDB(InputData i) + { + World.Instance.SendMessage("@wAvailable mob db commands:", i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb add") + " @w- Add a new mob to database.", i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb count") + " @w- Shows how many mobs you have in database.", + i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb delete") + " @w- Delete a mob from database.", i.ClientMask); + World.Instance.SendMessage("@Y" + string.Format("{0,-20}", "mobdb find") + " @w- Find mobs in database.", + i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb mobinfo") + " @w- Show information or edit a mob.", i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb save") + " @w- Save mob database to file.", i.ClientMask); + World.Instance.SendMessage( + "@Y" + string.Format("{0,-20}", "mobdb where") + " @w- Show mob locations.", i.ClientMask); + return true; + } + + private bool CommandAdd(InputData i) + { + string n; + if(!i.Arguments.Success || (n = i.Arguments.Groups[1].Value.Trim()).Length == 0) + { + World.Instance.SendMessage("@wSyntax: mobdb add ", i.ClientMask); + return true; + } + + if(UnknownMobs.Count == 0) + { + World.Instance.SendMessage("@wWe have no mobs with unknown longname in roomlist.", i.ClientMask); + return true; + } + + AddMobKeywords = n; + ChooseFromUnknown = 1; + World.Instance.Execute("lastkills 1", false); + return true; + } + + private bool CommandMobInfo(InputData i) + { + if(!i.Arguments.Success || i.Arguments.Groups[1].Length == 0) + { + World.Instance.SendMessage("@wSyntax: mobdb mobinfo [option] [value]", i.ClientMask); + World.Instance.SendMessage("", i.ClientMask); + World.Instance.SendMessage("@wAvailable options for mob:", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "addflag") + " @w- Add a flag to mob.", + i.ClientMask); + World.Instance.SendMessage( + "@W" + string.Format("{0,-15}", "removeflag") + " @w- Remove a flag from mob.", i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "name1") + " @w- Change (short)name of mob.", + i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "name2") + " @w- Change second (short)name of mob.", + i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "name3") + " @w- Change third (short)name of mob. (This goes all the way up to 999).", + i.ClientMask); + World.Instance.SendMessage( + "@W" + string.Format("{0,-15}", "areas") + + " @w- Set allowed areas for mob. Separate with space, enter all to allow all areas.", i.ClientMask); + World.Instance.SendMessage( + "@W" + string.Format("{0,-15}", "color") + " @w- Change default color of mob name. Enter default as value to set default color from config.", i.ClientMask); + World.Instance.SendMessage( + "@W" + string.Format("{0,-15}", "keywords") + " @w- Change keywords for mob.", i.ClientMask); + World.Instance.SendMessage( + "@W" + string.Format("{0,-15}", "longname") + " @w- Change longname (roomname) of mob.", + i.ClientMask); + World.Instance.SendMessage("@W" + string.Format("{0,-15}", "level") + " @w- Change level of mob.", + i.ClientMask); + World.Instance.SendMessage("@wEnter '@Wclear@w' as a shortname to remove that name from mob.", i.ClientMask); + return true; + } + + uint mobId; + if(!uint.TryParse(i.Arguments.Groups[1].Value, out mobId)) + { + World.Instance.SendMessage("@wInvalid mob ID given (@R" + i.Arguments.Groups[1].Value + "@w).", + i.ClientMask); + return true; + } + + Mob r = GetMob(mobId); + if(r == null) + { + World.Instance.SendMessage("@wNo such mob in database (@R" + mobId + "@w).", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[2].Length != 0) + { + string key, val; + string str = i.Arguments.Groups[2].Value.Trim(); + if(str.Contains(' ')) + { + key = str.Substring(0, str.IndexOf(' ')).ToLower(); + val = str.Substring(key.Length).Trim(); + + switch(key) + { + case "addflag": + r.AddFlag(val); + break; + + case "removeflag": + r.RemoveFlag(val); + break; + + case "name": + if(r.Name.Length == 0) + r.Name = new[] { val }; + else + r.Name[0] = val; + break; + + case "level": + { + int lvl; + if(int.TryParse(val, out lvl)) + r.Level = lvl; + } break; + + case "areas": + { + string[] spl = val.ToLower().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if(spl.Length > 0) + r.Areas = spl; + } break; + + case "keywords": + { + if(val.Length > 0) + r.Keyword = val; + } break; + + case "longname": + { + if(val == "clear") + r.Longname = ""; + else + r.Longname = val; + } break; + + case "color": + { + if(val == "clear" || val == "default" || val == "none") + r.DefaultColor = null; + else if(val.Length > 0) + r.DefaultColor = val; + } break; + } + + if(key.StartsWith("name") && key.Length > 4) + { + key = key.Substring(4); + int nth; + if(int.TryParse(key, out nth) && nth >= 1) + { + nth--; + List Names = r.Name.ToList(); + if(nth >= r.Name.Length) + { + if(val != "clear") + Names.Add(val); + } + else if(val != "clear") + Names[nth] = val; + else + Names.RemoveAt(nth); + + r.Name = Names.ToArray(); + } + } + } + } + + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", + i.ClientMask); + World.Instance.SendMessage("@w| @WEntry @w: @Y" + string.Format("{0,-55}", r.Entry) + "@w|", + i.ClientMask); + int k = 0; + foreach(string z in r.Name) + { + ++k; + string tag = "Name (" + k + ")"; + World.Instance.SendMessage( + "@w| @W" + string.Format("{0,-11}", tag) + " @w: @G" + + string.Format("{0,-55}", !string.IsNullOrEmpty(z) ? z : "Unknown") + "@w|", i.ClientMask); + } + string area = ""; + if(r.Areas != null) + { + foreach(string y in r.Areas) + { + if(area.Length > 0) + area += " "; + area += y; + } + } + World.Instance.SendMessage("@w| @WArea @w: @M" + string.Format("{0,-55}", area) + "@w|", i.ClientMask); + { + StringBuilder strFlags = new StringBuilder(); + if(r.Flags != null) + { + foreach(string x in r.Flags) + { + if(strFlags.Length > 0) + strFlags.Append(", "); + strFlags.Append(x); + } + } + + string[] spl = Utility.WrapColored(strFlags.ToString(), 54, 0); + for(int j = 0; j < spl.Length; j++) + { + if(j == 0) + World.Instance.SendMessage("@w| @WFlags @w: " + string.Format("{0,-55}", spl[j]) + "@w|", + i.ClientMask); + else + World.Instance.SendMessage("@w| : " + string.Format("{0,-55}", spl[j]) + "@w|", + i.ClientMask); + } + } + + World.Instance.SendMessage("@w| @WLongname @w: " + Utility.FormatColoredString(r.Longname, -55) + "@w|", + i.ClientMask); + World.Instance.SendMessage("@w| @WKeywords @w: " + string.Format("{0,-55}", r.Keyword) + "@w|", + i.ClientMask); + World.Instance.SendMessage("@w| @WLevel @w: @Y" + string.Format("{0,-55}", r.Level) + "@w|", + i.ClientMask); + + string defColor = Config.GetString("Mob.DefaultColor", "@y"); + if(string.IsNullOrEmpty(r.DefaultColor)) + defColor += "Default"; + else + defColor = r.DefaultColor + r.DefaultColor.Replace("@", "@@"); + World.Instance.SendMessage("@w| @WColor @w: " + Utility.FormatColoredString(defColor, -55) + "@w|", + i.ClientMask); + + World.Instance.SendMessage("@w+----------------------------------------------------------------------+", + i.ClientMask); + return true; + } + + private bool CommandFind(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: mobdb find [exact] [case] ", i.ClientMask); + return true; + } + + string name = i.Arguments.Groups[3].Value; + if(i.Arguments.Groups[2].Length == 0) + name = name.ToLower(); + else if(name.Length >= 1) + name = NormalizeName(name); + + World.Instance.SendMessage("@wSearched for '@W" + name + "@w'.", i.ClientMask); + List Found = new List(); + foreach(KeyValuePair x in IMobs) + { + if(i.Arguments.Groups[1].Length != 0) + { + if(i.Arguments.Groups[2].Length != 0) + { + if(x.Value.Name.Contains(name)) + Found.Add(x.Value); + } + else + { + foreach(string z in x.Value.Name) + { + if(z.ToLower() == name) + { + Found.Add(x.Value); + break; + } + } + } + } + else + { + if(i.Arguments.Groups[2].Length != 0) + { + if(x.Value.Name.Contains(name)) + Found.Add(x.Value); + } + else + { + foreach(string z in x.Value.Name) + { + if(z.ToLower().Contains(name)) + { + Found.Add(x.Value); + break; + } + } + } + } + } + + if(Found.Count != 0) + { + World.Instance.SendMessage("@WEntry Name Area Room", i.ClientMask); + World.Instance.SendMessage("@G====== ============================================= ============ ======", + i.ClientMask); + foreach(Mob x in Found) + { + string area = ""; + if(x.Areas != null) + { + foreach(string y in x.Areas) + { + if(area.Length != 0) + area += " "; + area += y; + } + } + string room = ""; + uint ro = x.GetBestRoom(); + if(ro != uint.MaxValue) + room = ro.ToString(); + World.Instance.SendMessage("@Y" + string.Format("{0,-6}", x.Entry) + " @c" + + string.Format( + "{0,-" + "=============================================".Length + "}", + x.Names) + " @g" + string.Format("{0,-12}", area) + " @Y" + room); + } + } + + World.Instance.SendMessage("@wFound @C" + Found.Count + " @wmob" + (Found.Count != 1 ? "s" : "") + ".", + i.ClientMask); + return true; + } + + private bool CommandDelete(InputData i) + { + if(!i.Arguments.Success) + { + World.Instance.SendMessage("@wSyntax: mobdb delete ", i.ClientMask); + World.Instance.SendMessage(" @wmobdb delete ", i.ClientMask); + World.Instance.SendMessage(" @wmobdb delete locations", i.ClientMask); + World.Instance.SendMessage(" @wmobdb delete all", i.ClientMask); + return true; + } + + if(i.Arguments.Groups[1].Value.ToLower().Trim() == "locations") + { + foreach(KeyValuePair x in IMobs) + { + x.Value.Locations.Clear(); + } + + World.Instance.SendMessage("@wDeleted all mob locations.", i.ClientMask); + return true; + } + + uint id; + if(uint.TryParse(i.Arguments.Groups[1].Value, out id)) + { + if(!IMobs.ContainsKey(id)) + World.Instance.SendMessage("@wNo such mob in database (@R" + id + "@w).", i.ClientMask); + else + { + World.Instance.SendMessage("@wDeleted '@G" + IMobs[id].Names + "@w'.", i.ClientMask); + foreach(MobLocation ml in IMobs[id].Locations) + { + RoomLocations[ml.RoomEntry].Remove(ml); + if(RoomLocations[ml.RoomEntry].Count == 0) + RoomLocations.Remove(ml.RoomEntry); + } + IMobs.Remove(id); + } + } + else + { + string key = i.Arguments.Groups[1].Value.ToLower().Trim(); + bool isConfirm = false; + if(key.Contains(' ')) + { + isConfirm = key.Substring(key.IndexOf(' ') + 1).Trim() == "confirm"; + key = key.Substring(0, key.IndexOf(' ')); + } + + if(key == "all" && !isConfirm) + { + World.Instance.SendMessage("@wAre you sure you wish to delete all mobs? Enter '@Wmobdb delete all confirm@w' to confirm.", i.ClientMask); + return true; + } + + List del = new List(); + foreach(KeyValuePair x in IMobs) + { + if(key == "all" || x.Value.Areas.Contains(key)) + del.Add(x.Key); + } + + foreach(uint x in del) + IMobs.Remove(x); + + World.Instance.SendMessage("@wDeleted @C" + del.Count + " @wmob" + (del.Count != 1 ? "s" : "") + ".", i.ClientMask); + } + return true; + } + + private bool CommandCount(InputData i) + { + int thisArea = 0; + int total = IMobs.Count; + if(!string.IsNullOrEmpty(RoomInfoArea)) + { + foreach(KeyValuePair x in IMobs) + { + if(x.Value.Areas == null || x.Value.Areas.Length == 0) + continue; + if(x.Value.Areas.Contains(RoomInfoArea)) + thisArea++; + } + } + World.Instance.SendMessage("@wYou have added @G" + thisArea + " @wmobs to database in this area.", + i.ClientMask); + World.Instance.SendMessage("@wYou have added @G" + total + " @wmobs to database in all areas.", i.ClientMask); + return true; + } + + private bool CommandSave(InputData i) + { + string fileName = DBFileName; + if(i.Arguments.Success) + fileName = i.Arguments.Groups[0].Value; + + Save(fileName); + World.Instance.SendMessage("@wSaved mob database to '@W" + fileName + "@w'.", i.ClientMask); + return true; + } + + #endregion + + #region Triggers + + private bool TriggerLastKills(TriggerData t) + { + if(ChooseFromUnknown == 1) + ChooseFromUnknown = 2; + return false; + } + + private bool TriggerLastKills2(TriggerData t) + { + if(ChooseFromUnknown == 0) + return false; + + AddMobName = t.Match.Groups[1].Value.Trim(); + if(!int.TryParse(t.Match.Groups[2].Value, out AddMobLevel) || UnknownMobs.Count == 0) + { + ChooseFromUnknown = 0; + return false; + } + AddMobArea = RoomInfoArea; + + int i = 1; + World.Instance.SendMessage("@wSelect mob longname from list (type number):"); + foreach(string x in UnknownMobs) + { + World.Instance.SendMessage("@W" + i + ". " + x); + i++; + } + return false; + } + + private bool TriggerRoomChars1(TriggerData t) + { + foundMobs.Clear(); + UnknownMobs.Clear(); + ListeningRoomChars = true; + if(Config.GetInt32("Tags.Gag", 1) != 0) + t.Msg.AuthMask = 0; + + CurrentTime = (uint)(DateTime.UtcNow - new DateTime(2012, 1, 1)).TotalSeconds; + return false; + } + + private bool TriggerRoomChars2(TriggerData t) + { + ListeningRoomChars = false; + if(Config.GetInt32("Tags.Gag", 1) != 0) + t.Msg.AuthMask = 0; + if(RoomInfoEntry != uint.MaxValue && RoomLocations.ContainsKey(RoomInfoEntry)) + { + foreach(MobLocation x in RoomLocations[RoomInfoEntry]) + { + Mob m = GetMob(x.MobEntry); + if(m != null && CurrentTime - x.LastVisited >= 600) + { + x.TimesVisited++; + if(foundMobs.Contains(m)) + x.TimesSeen++; + x.LastVisited = CurrentTime; + } + } + } + return false; + } + + private List foundMobs = new List(); + private bool TriggerRoomChars3(TriggerData t) + { + if(!ListeningRoomChars) + return false; + + string n = t.Msg.Msg; + Mob m = GetMob(ref n); + t.Msg.Msg = n; + + if(m == null) + return false; + + if(RoomInfoEntry != uint.MaxValue) + { + if(!foundMobs.Contains(m)) + foundMobs.Add(m); + + bool f = false; + foreach(MobLocation x in m.Locations) + { + if(x.RoomEntry != RoomInfoEntry) + continue; + + f = true; + if(CurrentTime - x.LastVisited >= 600) + x.CountSeen++; + break; + } + + if(!f) + { + MobLocation ml = new MobLocation(); + ml.RoomEntry = RoomInfoEntry; + ml.MobEntry = m.Entry; + ml.LastVisited = CurrentTime; + ml.TimesSeen = 0; + ml.TimesVisited = 0; + ml.CountSeen = 1; + m.Locations.Add(ml); + if(!RoomLocations.ContainsKey(RoomInfoEntry)) + RoomLocations[RoomInfoEntry] = new List(); + RoomLocations[RoomInfoEntry].Add(ml); + } + } + return false; + } + + private bool TriggerRoomInfoNum(TriggerData t) + { + if(ChooseFromUnknown != 0) + { + ChooseFromUnknown = 0; + World.Instance.SendMessage("@wChanged room. Mob adding was cancelled."); + } + UnknownMobs.Clear(); + uint i; + if(!uint.TryParse(t.Match.Groups[1].Value, out i)) + { + RoomInfoEntry = uint.MaxValue; + return false; + } + + RoomInfoEntry = i; + return false; + } + + private bool TriggerRoomInfoArea(TriggerData t) + { + RoomInfoArea = t.Match.Groups[1].Value.Trim(); + return false; + } + + #endregion + + #region Saving & Loading + + public override void Shutdown() + { + base.Shutdown(); + + Save(DBFileName); + } + + private void Load() + { + StreamReader f; + try + { + f = new StreamReader(DBFileName); + } + catch + { + // No database exists or we aren't allowed to read it. Make a new database. + return; + } + + Mob[] data; + try + { + DataContractSerializer x = new DataContractSerializer(typeof(Mob[])); + data = x.ReadObject(f.BaseStream) as Mob[]; + } + catch + { + f.Close(); + Log.Error("Failed loading mob database! File corrupted?"); + return; + } + + f.Close(); + + if(data == null) + return; + + foreach(Mob a in data) + { + if(a == null) + continue; + + IMobs[a.Entry] = a; + if(a.Entry > _guidMob && a.Entry != uint.MaxValue) + _guidMob = a.Entry; + + foreach(MobLocation m in a.Locations) + { + if(!RoomLocations.ContainsKey(m.RoomEntry)) + RoomLocations[m.RoomEntry] = new List(); + RoomLocations[m.RoomEntry].Add(m); + } + } + + // Successfully loaded a database. Now make a backup because we have a working copy at the moment. + File.Delete(DBFileBackup); + File.Copy(DBFileName, DBFileBackup); + } + + private void Save(string fileName) + { + if(IMobs.Count == 0) + return; + StreamWriter f = new StreamWriter(fileName, false); + + try + { + DataContractSerializer x = new DataContractSerializer(typeof(Mob[])); + x.WriteObject(f.BaseStream, IMobs.Values.ToArray()); + } + catch(Exception e) + { + f.Close(); + throw e; + } + + f.Close(); + if(Config.GetInt32("AutoSave", 0) != 0) + WhenSave = World.Instance.MSTime + Config.GetInt32("AutoSave", 0) * 1000; + } + + #endregion + + public override void Update(long msTime) + { + base.Update(msTime); + + if(WhenSave == 0 && Config.GetInt32("AutoSave", 0) != 0) + WhenSave = Config.GetInt32("AutoSave", 0) * 1000 + msTime; + else if(WhenSave > 0 && WhenSave <= msTime) + Save(DBFileName); + } + } + + [DataContract] + internal class MobLocation + { + [DataMember] + internal uint MobEntry; + + [DataMember] + internal uint RoomEntry; + + [DataMember] + internal uint TimesSeen; + + [DataMember] + internal uint CountSeen; + + [DataMember] + internal uint TimesVisited; + + [DataMember] + internal uint LastVisited; + } + + public class MobDBConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Mob.DefaultColor", "@y", "Default color for the mob. Color can be changed by other plugins"); + CreateSetting("Mob.Longname", "@w[@G$mob.level@w] $mob.color$mob.name @D($mob.keywords)", "Replace mob room name with this format. Set this to \"\" to disable mob replacing. You can have variables in the name such as:\n$mob.entry - This is the unique ID of mob (our assigned not mud).\n$mob.level - Level of the mob.\n$mob.name - Mob shortname, what appears when you are fighting it.\n$mob.longname - Mob room name.\n$mob.keywords - These are mob keywords.\n$mob.color - This is the color of the mob name. Can be set from other plugins or manually changed.\nEnter $$ to escape the $ character."); + CreateSetting("AutoSave", 0, "Save mob database every X seconds. For example enter 600 to save mob database every 10 minutes. Enter 0 to disable this feature. The database is also saved on shutdown of program. You can also type \"mobdb save\" to manually save the database."); + CreateSetting("Tags.Gag", 1, "Gag {roomchars} tags for clients."); + } + } +} diff --git a/MobDB/MobDB.csproj b/MobDB/MobDB.csproj new file mode 100644 index 0000000..992d9bc --- /dev/null +++ b/MobDB/MobDB.csproj @@ -0,0 +1,94 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {8A7FE055-E1B4-4967-9C76-CA46BA854E73} + Library + Properties + MobDB + MobDB + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.0 + + + 3.5 + + + 3.5 + + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/MobDB/Properties/.svn/all-wcprops b/MobDB/Properties/.svn/all-wcprops new file mode 100644 index 0000000..d327cc7 --- /dev/null +++ b/MobDB/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 39 +/svn/!svn/ver/30/trunk/MobDB/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/!svn/ver/30/trunk/MobDB/Properties/AssemblyInfo.cs +END diff --git a/MobDB/Properties/.svn/desktop.ini b/MobDB/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/Properties/.svn/dir-prop-base b/MobDB/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/MobDB/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/MobDB/Properties/.svn/entries b/MobDB/Properties/.svn/entries new file mode 100644 index 0000000..aca64f8 --- /dev/null +++ b/MobDB/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/MobDB/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-23T07:49:14.623991Z +30 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:43.020138Z +6b951bead6f1cf41e5815b8ee4489dd6 +2012-01-23T07:49:14.623991Z +30 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1422 + diff --git a/MobDB/Properties/.svn/prop-base/desktop.ini b/MobDB/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/Properties/.svn/props/desktop.ini b/MobDB/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/MobDB/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..302c561 --- /dev/null +++ b/MobDB/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MobDB")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MobDB")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("e96d05a9-e49d-43b6-8730-38cf3415d9b1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MobDB/Properties/.svn/text-base/desktop.ini b/MobDB/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/Properties/.svn/tmp/desktop.ini b/MobDB/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/Properties/.svn/tmp/prop-base/desktop.ini b/MobDB/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/Properties/.svn/tmp/props/desktop.ini b/MobDB/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/Properties/.svn/tmp/text-base/desktop.ini b/MobDB/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/Properties/AssemblyInfo.cs b/MobDB/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..302c561 --- /dev/null +++ b/MobDB/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MobDB")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MobDB")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("e96d05a9-e49d-43b6-8730-38cf3415d9b1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MobDB/Properties/desktop.ini b/MobDB/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MobDB/desktop.ini b/MobDB/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MobDB/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/.svn/all-wcprops b/MoonScript/.svn/all-wcprops new file mode 100644 index 0000000..0b9e1cd --- /dev/null +++ b/MoonScript/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 32 +/svn/!svn/ver/2/trunk/MoonScript +END +MoonScript.csproj +K 25 +svn:wc:ra_dav:version-url +V 50 +/svn/!svn/ver/2/trunk/MoonScript/MoonScript.csproj +END +MoonScript.cs +K 25 +svn:wc:ra_dav:version-url +V 46 +/svn/!svn/ver/2/trunk/MoonScript/MoonScript.cs +END diff --git a/MoonScript/.svn/desktop.ini b/MoonScript/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/.svn/dir-prop-base b/MoonScript/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/MoonScript/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/MoonScript/.svn/entries b/MoonScript/.svn/entries new file mode 100644 index 0000000..4bfe16f --- /dev/null +++ b/MoonScript/.svn/entries @@ -0,0 +1,99 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/MoonScript +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +Properties +dir + +MoonScript.csproj +file + + + + +2016-03-25T22:18:43.236138Z +15b79e5ac554c9750588f19864572afc +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2724 + +MoonScript.cs +file + + + + +2016-03-25T22:18:43.236138Z +392e5e192e1e4d5d03cc0444e5183cd7 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +14037 + diff --git a/MoonScript/.svn/prop-base/desktop.ini b/MoonScript/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/.svn/props/desktop.ini b/MoonScript/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/.svn/text-base/MoonScript.cs.svn-base b/MoonScript/.svn/text-base/MoonScript.cs.svn-base new file mode 100644 index 0000000..54c6170 --- /dev/null +++ b/MoonScript/.svn/text-base/MoonScript.cs.svn-base @@ -0,0 +1,329 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore.Scripting; +using ProxyCore.Output; +using ProxyCore.Input; +using ProxyCore; + +namespace MoonScript +{ + public class MoonScript : Plugin + { + public MoonScript() : base("moons", "Moon Script") + { + // Set up extra information (optional). + Author = "Duckbat"; + Version = 1; + Description = "Tracks the position of three moons and displays how many ticks are until a moon rises or falls. Also tracks, and if you want, alerts when all three moons are about to rise. You need to have been outside and seen all three moons rise or fall at least once for it to work."; + RequiredPlayerConfig.Add("noweather ON"); // Need this to see the moons. + UpdateUrl = "www.duckbat.com/plugins/update.moons.txt"; + Website = "www.duckbat.com/plugins/index.php?t=moons"; + + // Register some triggers needed for the moon script + RegisterTrigger("whiterise", "@WYou see the white moon rising in the west.", OnSeeMoon, TriggerFlags.NotRegex, 1000, 3); + RegisterTrigger("whitefall", "@WYou notice the white moon falling to the east.", OnSeeMoon, TriggerFlags.NotRegex, 1000, -3); + RegisterTrigger("greyrise", "@[wD]You see the grey moon rising in the east.", OnSeeMoon, TriggerFlags.None, 1000, 2); + RegisterTrigger("greyfall", "@[wD]You notice the grey moon falling to the west.", OnSeeMoon, TriggerFlags.None, 1000, -2); + RegisterTrigger("blackrise", "@BYou see the black moon rising in the east.", OnSeeMoon, TriggerFlags.NotRegex, 1000, 1); + RegisterTrigger("blackfall", "@BYou notice the black moon falling to the west.", OnSeeMoon, TriggerFlags.NotRegex, 1000, -1); + RegisterTrigger("tick", "$gmcp.comm.tick", OnTick, TriggerFlags.NotRegex); + + // Register commands to check on the moons + RegisterCommand("moons", @"^alert\s+(on|off)$", MoonsCommand, 4); + } + + private int AlertMoons = -1; + + /// + /// This command is set up so if you type moons without arguments you will see the status of all moons. + /// If you type argument "alert on" or "alert off" you will change whether the plugin will alert us + /// when three moons are about to rise. + /// + /// + /// + private bool MoonsCommand(InputData i) + { + // If arguments capture was success it means user typed "alert on" or "alert off" + if(i.Arguments.Success) + { + // If length of captured word is 2 then we must have typed "on" and not "off" + if(i.Arguments.Groups[1].Length == 2) + { + AlertMoons = 3; // Change this to any amount you like, you will be alerted this many ticks before moons rise. + World.Instance.SendMessage("@wYou will now be alerted " + AlertMoons + " tick" + (AlertMoons != 1 ? "s" : "") + " before three moons appear.", i.ClientMask); + } + else // Otherwise it's "off" + { + AlertMoons = -1; + World.Instance.SendMessage("@wYou will no longer be alerted of three moons.", i.ClientMask); + } + } + else + { + /* Only send to the client that typed this alias, other's don't need to see it. + * This is what i.ClientMask is here. If it was called from a plugin send to all + * connected clients. */ + World.Instance.SendMessage("@w+-----------------------------+", i.ClientMask); + World.Instance.SendMessage("@w| @gMOONS @w|", i.ClientMask); + World.Instance.SendMessage("@w+-----------------------------+", i.ClientMask); + if(!HasSeenMoon(MoonTypes.Black)) + World.Instance.SendMessage("@w| @cBlack@w: " + string.Format("{0,-21}", "Unknown") + "|", i.ClientMask); + else + { + int j = WhenAreMoonsUp(MoonTypes.Black); + if(j == 0) + { + j = HowLongAreMoonsUp(MoonTypes.Black); + World.Instance.SendMessage("@w| @cBlack@w: @G" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + else + World.Instance.SendMessage("@w| @cBlack@w: @W" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + if(!HasSeenMoon(MoonTypes.Grey)) + World.Instance.SendMessage("@w| @cGrey @w: " + string.Format("{0,-21}", "Unknown") + "|", i.ClientMask); + else + { + int j = WhenAreMoonsUp(MoonTypes.Grey); + if(j == 0) + { + j = HowLongAreMoonsUp(MoonTypes.Grey); + World.Instance.SendMessage("@w| @cGrey @w: @G" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + else + World.Instance.SendMessage("@w| @cGrey @w: @W" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + if(!HasSeenMoon(MoonTypes.White)) + World.Instance.SendMessage("@w| @cWhite@w: " + string.Format("{0,-21}", "Unknown") + "|", i.ClientMask); + else + { + int j = WhenAreMoonsUp(MoonTypes.White); + if(j == 0) + { + j = HowLongAreMoonsUp(MoonTypes.White); + World.Instance.SendMessage("@w| @cWhite@w: @G" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + else + World.Instance.SendMessage("@w| @cWhite@w: @W" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + if(!HasSeenMoon(MoonTypes.Black) || !HasSeenMoon(MoonTypes.Grey) || !HasSeenMoon(MoonTypes.White)) + World.Instance.SendMessage("@w| @cAll @w: " + string.Format("{0,-21}", "Unknown") + "|", i.ClientMask); + else + { + int j = WhenAreMoonsUp(MoonTypes.Black, MoonTypes.Grey, MoonTypes.White); + if(j == 0) + { + j = HowLongAreMoonsUp(MoonTypes.Black, MoonTypes.Grey, MoonTypes.White); + World.Instance.SendMessage("@w| @cAll @w: @G" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + else + { + string k = j + "(" + HowLongAreMoonsUp(MoonTypes.Black, MoonTypes.Grey, MoonTypes.White) + ")"; + World.Instance.SendMessage("@w| @cAll @w: @W" + string.Format("{0,-21}", k) + "@w|", + new[] {i.ClientId}); + } + } + World.Instance.SendMessage("@w+-----------------------------+", i.ClientMask); + if(AlertMoons == -1) + World.Instance.SendMessage("@wUse '@Wmoons alert on@w' to see alert when three moons are about to rise."); + else + World.Instance.SendMessage("@wUse '@Wmoons alert off@w' if you don't want to be notified of three moons."); + } + + return true; + } + + private bool OnSeeMoon(TriggerData t) + { + /* Arg here is what we used to register trigger with, for example if you see whiterise trigger + * I set the argument to be 3 so it will go to case 3: */ + switch(t.Arg) + { + case 3: + MoonTimer[(int)MoonTypes.White] = MoonDuration[(int)MoonTypes.White] - 1; + break; + case -3: + MoonTimer[(int)MoonTypes.White] = 0; + break; + + case 2: + MoonTimer[(int)MoonTypes.Grey] = MoonDuration[(int)MoonTypes.Grey] - 1; + break; + case -2: + MoonTimer[(int)MoonTypes.Grey] = 0; + break; + + case 1: + MoonTimer[(int)MoonTypes.Black] = MoonDuration[(int)MoonTypes.Black] - 1; + break; + case -1: + MoonTimer[(int)MoonTypes.Black] = 0; + break; + } + return false; + } + + /// + /// Full interval of moons (black, grey, white). + /// + private readonly int[] MoonInterval = new[] { 50, 30, 65 }; + + /// + /// Duration of the moons, this is inverted so duration is actually MoonInterval[i] - MoonDuration[i]. + /// + private readonly int[] MoonDuration = new[] { 39, 24, 50 }; + + /// + /// Current tick timers on the moons. -1 means unknown haven't seen moon yet. + /// + private int[] MoonTimer = new[] { -1, -1, -1 }; + + private bool OnTick(TriggerData t) + { + for(int i = 0; i < MoonTimer.Length; i++) + { + // Increase moon timers if we have seen a moon rise or fall. + if(MoonTimer[i] == -1) + continue; + MoonTimer[i]++; + MoonTimer[i] %= MoonInterval[i]; + } + + // See if we must alert users when moons are about to come up. + if(AlertMoons != -1) + { + int i = WhenAreMoonsUp(MoonTypes.Black, MoonTypes.Grey, MoonTypes.White); + if(i == 0) + { + if(!IsMoonUp(MoonTypes.Black, -1) || !IsMoonUp(MoonTypes.Grey, -1) || !IsMoonUp(MoonTypes.White, -1)) + World.Instance.SendMessage("@GMOONS: @wThree moons are now @WUP@w!"); + } + else if(i <= AlertMoons) + { + World.Instance.SendMessage("@GMOONS: @wThree moons will be up in @W" + i + " @wtick" + (i != 1 ? "s" : "") + "."); + } + } + return false; + } + + private void Reset() + { + // Reset moon timers to unknown, this will be called if we lose connection. + MoonTimer = new[] { -1, -1, -1 }; + } + + public override void OnDisconnect() + { + base.OnDisconnect(); + Reset(); + } + + /// + /// Check if moon is up. + /// + /// Moon type to check for. + /// Offset to current tick. For example 0 would check if moon is currently up but 1 would check if moon is up in 1 tick. + /// + public bool IsMoonUp(MoonTypes i, int tickOffset) + { + if(MoonTimer[(int)i] == -1) + return false; + return (MoonTimer[(int)i] + tickOffset) % MoonInterval[(int)i] >= MoonDuration[(int)i] - 1; + } + + /// + /// Check if we have seen a moon rise or fall. + /// + /// Moon type to check for. + /// + public bool HasSeenMoon(MoonTypes i) + { + return MoonTimer[(int)i] != -1; + } + + /// + /// Check in how many ticks are all these moons up. + /// + /// Moons types to check for. Can be more than one or all. + /// + public int WhenAreMoonsUp(params MoonTypes[] args) + { + if(args.Length == 0) + return 0; + + foreach(MoonTypes t in args) + { + if(!HasSeenMoon(t)) + return -1; + } + + int i = 0; + while(true) + { + bool are = true; + for(int j = 0; j < args.Length; j++) + { + if(!IsMoonUp(args[j], i)) + { + are = false; + break; + } + } + + if(!are) + { + i++; + continue; + } + + return i; + } + } + + /// + /// Next time these moons are up how long are they up for (how many ticks). + /// + /// Moons to check for. Can be more than one or all. + /// + public int HowLongAreMoonsUp(params MoonTypes[] args) + { + if(args.Length == 0) + return 0; + + foreach(MoonTypes t in args) + { + if(!HasSeenMoon(t)) + return -1; + } + + int i = WhenAreMoonsUp(args); + int k = 0; + while(true) + { + bool are = true; + for(int j = 0; j < args.Length; j++) + { + if(!IsMoonUp(args[j], i)) + { + are = false; + break; + } + } + + if(!are) + return k; + + k++; + i++; + } + } + } + + public enum MoonTypes + { + Black = 0, + Grey = 1, + White = 2, + } +} diff --git a/MoonScript/.svn/text-base/MoonScript.csproj.svn-base b/MoonScript/.svn/text-base/MoonScript.csproj.svn-base new file mode 100644 index 0000000..9b2b804 --- /dev/null +++ b/MoonScript/.svn/text-base/MoonScript.csproj.svn-base @@ -0,0 +1,63 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {884C4C56-29D1-4761-B3B8-3DC2BF19D54C} + Library + Properties + MoonScript + MoonScript + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/MoonScript/.svn/text-base/desktop.ini b/MoonScript/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/.svn/tmp/desktop.ini b/MoonScript/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/.svn/tmp/prop-base/desktop.ini b/MoonScript/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/.svn/tmp/props/desktop.ini b/MoonScript/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/.svn/tmp/text-base/desktop.ini b/MoonScript/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/MoonScript.cs b/MoonScript/MoonScript.cs new file mode 100644 index 0000000..54c6170 --- /dev/null +++ b/MoonScript/MoonScript.cs @@ -0,0 +1,329 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore.Scripting; +using ProxyCore.Output; +using ProxyCore.Input; +using ProxyCore; + +namespace MoonScript +{ + public class MoonScript : Plugin + { + public MoonScript() : base("moons", "Moon Script") + { + // Set up extra information (optional). + Author = "Duckbat"; + Version = 1; + Description = "Tracks the position of three moons and displays how many ticks are until a moon rises or falls. Also tracks, and if you want, alerts when all three moons are about to rise. You need to have been outside and seen all three moons rise or fall at least once for it to work."; + RequiredPlayerConfig.Add("noweather ON"); // Need this to see the moons. + UpdateUrl = "www.duckbat.com/plugins/update.moons.txt"; + Website = "www.duckbat.com/plugins/index.php?t=moons"; + + // Register some triggers needed for the moon script + RegisterTrigger("whiterise", "@WYou see the white moon rising in the west.", OnSeeMoon, TriggerFlags.NotRegex, 1000, 3); + RegisterTrigger("whitefall", "@WYou notice the white moon falling to the east.", OnSeeMoon, TriggerFlags.NotRegex, 1000, -3); + RegisterTrigger("greyrise", "@[wD]You see the grey moon rising in the east.", OnSeeMoon, TriggerFlags.None, 1000, 2); + RegisterTrigger("greyfall", "@[wD]You notice the grey moon falling to the west.", OnSeeMoon, TriggerFlags.None, 1000, -2); + RegisterTrigger("blackrise", "@BYou see the black moon rising in the east.", OnSeeMoon, TriggerFlags.NotRegex, 1000, 1); + RegisterTrigger("blackfall", "@BYou notice the black moon falling to the west.", OnSeeMoon, TriggerFlags.NotRegex, 1000, -1); + RegisterTrigger("tick", "$gmcp.comm.tick", OnTick, TriggerFlags.NotRegex); + + // Register commands to check on the moons + RegisterCommand("moons", @"^alert\s+(on|off)$", MoonsCommand, 4); + } + + private int AlertMoons = -1; + + /// + /// This command is set up so if you type moons without arguments you will see the status of all moons. + /// If you type argument "alert on" or "alert off" you will change whether the plugin will alert us + /// when three moons are about to rise. + /// + /// + /// + private bool MoonsCommand(InputData i) + { + // If arguments capture was success it means user typed "alert on" or "alert off" + if(i.Arguments.Success) + { + // If length of captured word is 2 then we must have typed "on" and not "off" + if(i.Arguments.Groups[1].Length == 2) + { + AlertMoons = 3; // Change this to any amount you like, you will be alerted this many ticks before moons rise. + World.Instance.SendMessage("@wYou will now be alerted " + AlertMoons + " tick" + (AlertMoons != 1 ? "s" : "") + " before three moons appear.", i.ClientMask); + } + else // Otherwise it's "off" + { + AlertMoons = -1; + World.Instance.SendMessage("@wYou will no longer be alerted of three moons.", i.ClientMask); + } + } + else + { + /* Only send to the client that typed this alias, other's don't need to see it. + * This is what i.ClientMask is here. If it was called from a plugin send to all + * connected clients. */ + World.Instance.SendMessage("@w+-----------------------------+", i.ClientMask); + World.Instance.SendMessage("@w| @gMOONS @w|", i.ClientMask); + World.Instance.SendMessage("@w+-----------------------------+", i.ClientMask); + if(!HasSeenMoon(MoonTypes.Black)) + World.Instance.SendMessage("@w| @cBlack@w: " + string.Format("{0,-21}", "Unknown") + "|", i.ClientMask); + else + { + int j = WhenAreMoonsUp(MoonTypes.Black); + if(j == 0) + { + j = HowLongAreMoonsUp(MoonTypes.Black); + World.Instance.SendMessage("@w| @cBlack@w: @G" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + else + World.Instance.SendMessage("@w| @cBlack@w: @W" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + if(!HasSeenMoon(MoonTypes.Grey)) + World.Instance.SendMessage("@w| @cGrey @w: " + string.Format("{0,-21}", "Unknown") + "|", i.ClientMask); + else + { + int j = WhenAreMoonsUp(MoonTypes.Grey); + if(j == 0) + { + j = HowLongAreMoonsUp(MoonTypes.Grey); + World.Instance.SendMessage("@w| @cGrey @w: @G" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + else + World.Instance.SendMessage("@w| @cGrey @w: @W" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + if(!HasSeenMoon(MoonTypes.White)) + World.Instance.SendMessage("@w| @cWhite@w: " + string.Format("{0,-21}", "Unknown") + "|", i.ClientMask); + else + { + int j = WhenAreMoonsUp(MoonTypes.White); + if(j == 0) + { + j = HowLongAreMoonsUp(MoonTypes.White); + World.Instance.SendMessage("@w| @cWhite@w: @G" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + else + World.Instance.SendMessage("@w| @cWhite@w: @W" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + if(!HasSeenMoon(MoonTypes.Black) || !HasSeenMoon(MoonTypes.Grey) || !HasSeenMoon(MoonTypes.White)) + World.Instance.SendMessage("@w| @cAll @w: " + string.Format("{0,-21}", "Unknown") + "|", i.ClientMask); + else + { + int j = WhenAreMoonsUp(MoonTypes.Black, MoonTypes.Grey, MoonTypes.White); + if(j == 0) + { + j = HowLongAreMoonsUp(MoonTypes.Black, MoonTypes.Grey, MoonTypes.White); + World.Instance.SendMessage("@w| @cAll @w: @G" + string.Format("{0,-21}", j) + "@w|", i.ClientMask); + } + else + { + string k = j + "(" + HowLongAreMoonsUp(MoonTypes.Black, MoonTypes.Grey, MoonTypes.White) + ")"; + World.Instance.SendMessage("@w| @cAll @w: @W" + string.Format("{0,-21}", k) + "@w|", + new[] {i.ClientId}); + } + } + World.Instance.SendMessage("@w+-----------------------------+", i.ClientMask); + if(AlertMoons == -1) + World.Instance.SendMessage("@wUse '@Wmoons alert on@w' to see alert when three moons are about to rise."); + else + World.Instance.SendMessage("@wUse '@Wmoons alert off@w' if you don't want to be notified of three moons."); + } + + return true; + } + + private bool OnSeeMoon(TriggerData t) + { + /* Arg here is what we used to register trigger with, for example if you see whiterise trigger + * I set the argument to be 3 so it will go to case 3: */ + switch(t.Arg) + { + case 3: + MoonTimer[(int)MoonTypes.White] = MoonDuration[(int)MoonTypes.White] - 1; + break; + case -3: + MoonTimer[(int)MoonTypes.White] = 0; + break; + + case 2: + MoonTimer[(int)MoonTypes.Grey] = MoonDuration[(int)MoonTypes.Grey] - 1; + break; + case -2: + MoonTimer[(int)MoonTypes.Grey] = 0; + break; + + case 1: + MoonTimer[(int)MoonTypes.Black] = MoonDuration[(int)MoonTypes.Black] - 1; + break; + case -1: + MoonTimer[(int)MoonTypes.Black] = 0; + break; + } + return false; + } + + /// + /// Full interval of moons (black, grey, white). + /// + private readonly int[] MoonInterval = new[] { 50, 30, 65 }; + + /// + /// Duration of the moons, this is inverted so duration is actually MoonInterval[i] - MoonDuration[i]. + /// + private readonly int[] MoonDuration = new[] { 39, 24, 50 }; + + /// + /// Current tick timers on the moons. -1 means unknown haven't seen moon yet. + /// + private int[] MoonTimer = new[] { -1, -1, -1 }; + + private bool OnTick(TriggerData t) + { + for(int i = 0; i < MoonTimer.Length; i++) + { + // Increase moon timers if we have seen a moon rise or fall. + if(MoonTimer[i] == -1) + continue; + MoonTimer[i]++; + MoonTimer[i] %= MoonInterval[i]; + } + + // See if we must alert users when moons are about to come up. + if(AlertMoons != -1) + { + int i = WhenAreMoonsUp(MoonTypes.Black, MoonTypes.Grey, MoonTypes.White); + if(i == 0) + { + if(!IsMoonUp(MoonTypes.Black, -1) || !IsMoonUp(MoonTypes.Grey, -1) || !IsMoonUp(MoonTypes.White, -1)) + World.Instance.SendMessage("@GMOONS: @wThree moons are now @WUP@w!"); + } + else if(i <= AlertMoons) + { + World.Instance.SendMessage("@GMOONS: @wThree moons will be up in @W" + i + " @wtick" + (i != 1 ? "s" : "") + "."); + } + } + return false; + } + + private void Reset() + { + // Reset moon timers to unknown, this will be called if we lose connection. + MoonTimer = new[] { -1, -1, -1 }; + } + + public override void OnDisconnect() + { + base.OnDisconnect(); + Reset(); + } + + /// + /// Check if moon is up. + /// + /// Moon type to check for. + /// Offset to current tick. For example 0 would check if moon is currently up but 1 would check if moon is up in 1 tick. + /// + public bool IsMoonUp(MoonTypes i, int tickOffset) + { + if(MoonTimer[(int)i] == -1) + return false; + return (MoonTimer[(int)i] + tickOffset) % MoonInterval[(int)i] >= MoonDuration[(int)i] - 1; + } + + /// + /// Check if we have seen a moon rise or fall. + /// + /// Moon type to check for. + /// + public bool HasSeenMoon(MoonTypes i) + { + return MoonTimer[(int)i] != -1; + } + + /// + /// Check in how many ticks are all these moons up. + /// + /// Moons types to check for. Can be more than one or all. + /// + public int WhenAreMoonsUp(params MoonTypes[] args) + { + if(args.Length == 0) + return 0; + + foreach(MoonTypes t in args) + { + if(!HasSeenMoon(t)) + return -1; + } + + int i = 0; + while(true) + { + bool are = true; + for(int j = 0; j < args.Length; j++) + { + if(!IsMoonUp(args[j], i)) + { + are = false; + break; + } + } + + if(!are) + { + i++; + continue; + } + + return i; + } + } + + /// + /// Next time these moons are up how long are they up for (how many ticks). + /// + /// Moons to check for. Can be more than one or all. + /// + public int HowLongAreMoonsUp(params MoonTypes[] args) + { + if(args.Length == 0) + return 0; + + foreach(MoonTypes t in args) + { + if(!HasSeenMoon(t)) + return -1; + } + + int i = WhenAreMoonsUp(args); + int k = 0; + while(true) + { + bool are = true; + for(int j = 0; j < args.Length; j++) + { + if(!IsMoonUp(args[j], i)) + { + are = false; + break; + } + } + + if(!are) + return k; + + k++; + i++; + } + } + } + + public enum MoonTypes + { + Black = 0, + Grey = 1, + White = 2, + } +} diff --git a/MoonScript/MoonScript.csproj b/MoonScript/MoonScript.csproj new file mode 100644 index 0000000..179a7cb --- /dev/null +++ b/MoonScript/MoonScript.csproj @@ -0,0 +1,90 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {884C4C56-29D1-4761-B3B8-3DC2BF19D54C} + Library + Properties + MoonScript + MoonScript + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/MoonScript/Properties/.svn/all-wcprops b/MoonScript/Properties/.svn/all-wcprops new file mode 100644 index 0000000..03d956d --- /dev/null +++ b/MoonScript/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 43 +/svn/!svn/ver/2/trunk/MoonScript/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 59 +/svn/!svn/ver/2/trunk/MoonScript/Properties/AssemblyInfo.cs +END diff --git a/MoonScript/Properties/.svn/desktop.ini b/MoonScript/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/Properties/.svn/dir-prop-base b/MoonScript/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/MoonScript/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/MoonScript/Properties/.svn/entries b/MoonScript/Properties/.svn/entries new file mode 100644 index 0000000..b9378e4 --- /dev/null +++ b/MoonScript/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/MoonScript/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:43.236138Z +a6f52db3a0f64151a4dafb0b208c1c07 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1432 + diff --git a/MoonScript/Properties/.svn/prop-base/desktop.ini b/MoonScript/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/Properties/.svn/props/desktop.ini b/MoonScript/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/MoonScript/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..2f5ac7f --- /dev/null +++ b/MoonScript/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MoonScript")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MoonScript")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("81ed5fc1-054a-4f5a-af4b-a5fa2aedf490")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MoonScript/Properties/.svn/text-base/desktop.ini b/MoonScript/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/Properties/.svn/tmp/desktop.ini b/MoonScript/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/Properties/.svn/tmp/prop-base/desktop.ini b/MoonScript/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/Properties/.svn/tmp/props/desktop.ini b/MoonScript/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/Properties/.svn/tmp/text-base/desktop.ini b/MoonScript/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/Properties/AssemblyInfo.cs b/MoonScript/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2f5ac7f --- /dev/null +++ b/MoonScript/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MoonScript")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MoonScript")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("81ed5fc1-054a-4f5a-af4b-a5fa2aedf490")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MoonScript/Properties/desktop.ini b/MoonScript/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MoonScript/desktop.ini b/MoonScript/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MoonScript/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/.svn/all-wcprops b/MudLog/.svn/all-wcprops new file mode 100644 index 0000000..60aea3e --- /dev/null +++ b/MudLog/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 28 +/svn/!svn/ver/2/trunk/MudLog +END +MudLog.cs +K 25 +svn:wc:ra_dav:version-url +V 38 +/svn/!svn/ver/2/trunk/MudLog/MudLog.cs +END +MudLog.csproj +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/2/trunk/MudLog/MudLog.csproj +END diff --git a/MudLog/.svn/desktop.ini b/MudLog/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/.svn/dir-prop-base b/MudLog/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/MudLog/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/MudLog/.svn/entries b/MudLog/.svn/entries new file mode 100644 index 0000000..9ef0dcc --- /dev/null +++ b/MudLog/.svn/entries @@ -0,0 +1,99 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/MudLog +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +MudLog.csproj +file + + + + +2016-03-25T22:18:43.144138Z +dd2dd3662a0b51bed88e462b8aa7075a +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2712 + +MudLog.cs +file + + + + +2016-03-25T22:18:43.144138Z +627c80ade0e5117d641835c4f2771f4c +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +4320 + +Properties +dir + diff --git a/MudLog/.svn/prop-base/desktop.ini b/MudLog/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/.svn/props/desktop.ini b/MudLog/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/.svn/text-base/MudLog.cs.svn-base b/MudLog/.svn/text-base/MudLog.cs.svn-base new file mode 100644 index 0000000..c0e3fe7 --- /dev/null +++ b/MudLog/.svn/text-base/MudLog.cs.svn-base @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using ProxyCore; +using ProxyCore.Scripting; + +namespace MudLog +{ + public class MudLog : Plugin + { + public MudLog() + : base("mudlog", "MUD Logger") + { + Author = "Duckbat"; + Version = 1; + Description = "This plugin will log (write down into a file) all text that comes from MUD so you can later look at it if you like."; + UpdateUrl = "www.duckbat.com/plugins/update.mudlog.txt"; + Website = "www.duckbat.com/plugins/index.php?t=mudlog"; + + Config = new MudLogConfig(); + } + + private StreamWriter f; + + public override void OnLoadedConfig(bool Success) + { + base.OnLoadedConfig(Success); + + if(Config.GetInt32("Log.Enabled", 1) != 0) + { + string fn = Config.GetString("Log.Filename", "log.{year}-{month}-{day}.{hour}-{minute}-{second}.txt"); + fn = fn.Replace("{year}", DateTime.Now.Year.ToString()); + fn = fn.Replace("{month}", DateTime.Now.Month.ToString()); + fn = fn.Replace("{day}", DateTime.Now.Day.ToString()); + fn = fn.Replace("{hour}", DateTime.Now.Hour.ToString()); + fn = fn.Replace("{minute}", DateTime.Now.Minute.ToString()); + fn = fn.Replace("{second}", DateTime.Now.Second.ToString()); + + if(fn.Contains("/")) + fn = fn.Substring(fn.LastIndexOf("/") + 1); + if(fn.Contains("\\")) + fn = fn.Substring(fn.LastIndexOf("\\") + 1); + + if(string.IsNullOrEmpty(fn)) + return; + + if(!Directory.Exists("logs")) + { + try + { + Directory.CreateDirectory("logs"); + } + catch + { + return; + } + } + + f = new StreamWriter("logs/" + fn, true); + } + } + + public override void OnReceivedLineAfter(ProxyCore.Messages.Message Msg) + { + base.OnReceivedLineAfter(Msg); + + if(f != null) + { + string m = Msg.Msg; + if(Config.GetInt32("Log.StripColors", 0) != 0) + m = Colors.RemoveColors(m, false); + f.WriteLine( + string.Format("[" + "{0:D2}" + ":" + "{1:D2}" + ":" + "{2:D2}" + "] ", DateTime.Now.Hour, + DateTime.Now.Minute, DateTime.Now.Second) + m); + } + } + + public override void OnEnteredCommandAfter(ref string Msg, uint ClientId, int AuthLevel) + { + base.OnEnteredCommandAfter(ref Msg, ClientId, AuthLevel); + + if(f != null && Config.GetInt32("Log.Commands", 0) != 0) + f.WriteLine(string.Format("[" + "{0:D2}" + ":" + "{1:D2}" + ":" + "{2:D2}" + "] Sent to MUD: ", DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second) + Msg); + } + + public override void Shutdown() + { + base.Shutdown(); + + if(f != null) + f.Close(); + } + } + + public class MudLogConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Log.Enabled", 1, "Enable logging of all what happens in MUD to a file."); + CreateSetting("Log.Filename", "log.{year}-{month}-{day}.{hour}-{minute}-{second}.txt", "File name of log. You can use {year}, {month}, {day}, {hour}, {minute}, {second} in the filename. If a log file with this name already exists in logs folder then it will be appended to. Be careful however if the log file gets too big this will become slow so it's highly recommended that you differentiate them by including the timestamp keywords somehow."); + CreateSetting("Log.Commands", 0, "Log commands sent to MUD (not ones that were handled by plugins)."); + CreateSetting("Log.StripColors", 0, "Remove all color codes when logging."); + } + } +} diff --git a/MudLog/.svn/text-base/MudLog.csproj.svn-base b/MudLog/.svn/text-base/MudLog.csproj.svn-base new file mode 100644 index 0000000..54f4d2f --- /dev/null +++ b/MudLog/.svn/text-base/MudLog.csproj.svn-base @@ -0,0 +1,63 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {2268C185-E9CC-409D-BE6F-A50ED4B900D7} + Library + Properties + MudLog + MudLog + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/MudLog/.svn/text-base/desktop.ini b/MudLog/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/.svn/tmp/desktop.ini b/MudLog/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/.svn/tmp/prop-base/desktop.ini b/MudLog/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/.svn/tmp/props/desktop.ini b/MudLog/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/.svn/tmp/text-base/desktop.ini b/MudLog/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/MudLog.cs b/MudLog/MudLog.cs new file mode 100644 index 0000000..c0e3fe7 --- /dev/null +++ b/MudLog/MudLog.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using ProxyCore; +using ProxyCore.Scripting; + +namespace MudLog +{ + public class MudLog : Plugin + { + public MudLog() + : base("mudlog", "MUD Logger") + { + Author = "Duckbat"; + Version = 1; + Description = "This plugin will log (write down into a file) all text that comes from MUD so you can later look at it if you like."; + UpdateUrl = "www.duckbat.com/plugins/update.mudlog.txt"; + Website = "www.duckbat.com/plugins/index.php?t=mudlog"; + + Config = new MudLogConfig(); + } + + private StreamWriter f; + + public override void OnLoadedConfig(bool Success) + { + base.OnLoadedConfig(Success); + + if(Config.GetInt32("Log.Enabled", 1) != 0) + { + string fn = Config.GetString("Log.Filename", "log.{year}-{month}-{day}.{hour}-{minute}-{second}.txt"); + fn = fn.Replace("{year}", DateTime.Now.Year.ToString()); + fn = fn.Replace("{month}", DateTime.Now.Month.ToString()); + fn = fn.Replace("{day}", DateTime.Now.Day.ToString()); + fn = fn.Replace("{hour}", DateTime.Now.Hour.ToString()); + fn = fn.Replace("{minute}", DateTime.Now.Minute.ToString()); + fn = fn.Replace("{second}", DateTime.Now.Second.ToString()); + + if(fn.Contains("/")) + fn = fn.Substring(fn.LastIndexOf("/") + 1); + if(fn.Contains("\\")) + fn = fn.Substring(fn.LastIndexOf("\\") + 1); + + if(string.IsNullOrEmpty(fn)) + return; + + if(!Directory.Exists("logs")) + { + try + { + Directory.CreateDirectory("logs"); + } + catch + { + return; + } + } + + f = new StreamWriter("logs/" + fn, true); + } + } + + public override void OnReceivedLineAfter(ProxyCore.Messages.Message Msg) + { + base.OnReceivedLineAfter(Msg); + + if(f != null) + { + string m = Msg.Msg; + if(Config.GetInt32("Log.StripColors", 0) != 0) + m = Colors.RemoveColors(m, false); + f.WriteLine( + string.Format("[" + "{0:D2}" + ":" + "{1:D2}" + ":" + "{2:D2}" + "] ", DateTime.Now.Hour, + DateTime.Now.Minute, DateTime.Now.Second) + m); + } + } + + public override void OnEnteredCommandAfter(ref string Msg, uint ClientId, int AuthLevel) + { + base.OnEnteredCommandAfter(ref Msg, ClientId, AuthLevel); + + if(f != null && Config.GetInt32("Log.Commands", 0) != 0) + f.WriteLine(string.Format("[" + "{0:D2}" + ":" + "{1:D2}" + ":" + "{2:D2}" + "] Sent to MUD: ", DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second) + Msg); + } + + public override void Shutdown() + { + base.Shutdown(); + + if(f != null) + f.Close(); + } + } + + public class MudLogConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Log.Enabled", 1, "Enable logging of all what happens in MUD to a file."); + CreateSetting("Log.Filename", "log.{year}-{month}-{day}.{hour}-{minute}-{second}.txt", "File name of log. You can use {year}, {month}, {day}, {hour}, {minute}, {second} in the filename. If a log file with this name already exists in logs folder then it will be appended to. Be careful however if the log file gets too big this will become slow so it's highly recommended that you differentiate them by including the timestamp keywords somehow."); + CreateSetting("Log.Commands", 0, "Log commands sent to MUD (not ones that were handled by plugins)."); + CreateSetting("Log.StripColors", 0, "Remove all color codes when logging."); + } + } +} diff --git a/MudLog/MudLog.csproj b/MudLog/MudLog.csproj new file mode 100644 index 0000000..349f644 --- /dev/null +++ b/MudLog/MudLog.csproj @@ -0,0 +1,90 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {2268C185-E9CC-409D-BE6F-A50ED4B900D7} + Library + Properties + MudLog + MudLog + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/MudLog/Properties/.svn/all-wcprops b/MudLog/Properties/.svn/all-wcprops new file mode 100644 index 0000000..08d8b1b --- /dev/null +++ b/MudLog/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 39 +/svn/!svn/ver/2/trunk/MudLog/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/!svn/ver/2/trunk/MudLog/Properties/AssemblyInfo.cs +END diff --git a/MudLog/Properties/.svn/desktop.ini b/MudLog/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/Properties/.svn/dir-prop-base b/MudLog/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/MudLog/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/MudLog/Properties/.svn/entries b/MudLog/Properties/.svn/entries new file mode 100644 index 0000000..a8a6d7c --- /dev/null +++ b/MudLog/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/MudLog/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:43.140138Z +3984ebfff866f267b9fe8cf07db1cdaf +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1424 + diff --git a/MudLog/Properties/.svn/prop-base/desktop.ini b/MudLog/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/Properties/.svn/props/desktop.ini b/MudLog/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/MudLog/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..09be98f --- /dev/null +++ b/MudLog/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MudLog")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MudLog")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("b19209a5-d989-44b1-8bab-6a9b87fb9477")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MudLog/Properties/.svn/text-base/desktop.ini b/MudLog/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/Properties/.svn/tmp/desktop.ini b/MudLog/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/Properties/.svn/tmp/prop-base/desktop.ini b/MudLog/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/Properties/.svn/tmp/props/desktop.ini b/MudLog/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/Properties/.svn/tmp/text-base/desktop.ini b/MudLog/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/Properties/AssemblyInfo.cs b/MudLog/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..09be98f --- /dev/null +++ b/MudLog/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MudLog")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MudLog")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("b19209a5-d989-44b1-8bab-6a9b87fb9477")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MudLog/Properties/desktop.ini b/MudLog/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MudLog/desktop.ini b/MudLog/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MudLog/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MySQL/.svn/all-wcprops b/MySQL/.svn/all-wcprops new file mode 100644 index 0000000..3264365 --- /dev/null +++ b/MySQL/.svn/all-wcprops @@ -0,0 +1,29 @@ +K 25 +svn:wc:ra_dav:version-url +V 28 +/svn/!svn/ver/58/trunk/MySQL +END +MySQL.csproj +K 25 +svn:wc:ra_dav:version-url +V 41 +/svn/!svn/ver/58/trunk/MySQL/MySQL.csproj +END +MySql.Data.dll +K 25 +svn:wc:ra_dav:version-url +V 43 +/svn/!svn/ver/58/trunk/MySQL/MySql.Data.dll +END +MySQL.cs +K 25 +svn:wc:ra_dav:version-url +V 37 +/svn/!svn/ver/58/trunk/MySQL/MySQL.cs +END +MySql.Data.Entity.dll +K 25 +svn:wc:ra_dav:version-url +V 50 +/svn/!svn/ver/58/trunk/MySQL/MySql.Data.Entity.dll +END diff --git a/MySQL/.svn/desktop.ini b/MySQL/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MySQL/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MySQL/.svn/dir-prop-base b/MySQL/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/MySQL/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/MySQL/.svn/entries b/MySQL/.svn/entries new file mode 100644 index 0000000..e1ed965 --- /dev/null +++ b/MySQL/.svn/entries @@ -0,0 +1,167 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/MySQL +http://proxymud.googlecode.com/svn + + + +2012-03-20T06:14:18.138919Z +58 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +MySQL.csproj +file + + + + +2016-03-25T22:18:43.104138Z +95d3f202c08dba38194188c3fed82ee3 +2012-03-20T06:14:18.138919Z +58 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2953 + +MySql.Data.dll +file + + + + +2016-03-25T22:18:43.104138Z +43336eb748b73004368a48ac4ddbfc98 +2012-03-20T06:14:18.138919Z +58 +default8p@gmail.com +has-props + + + + + + + + + + + + + + + + + + + + +369152 + +MySQL.cs +file + + + + +2016-03-25T22:18:43.104138Z +000f2d6f6d34d645af2addb73131cb0f +2012-03-20T06:14:18.138919Z +58 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +13683 + +Properties +dir + +MySql.Data.Entity.dll +file + + + + +2016-03-25T22:18:43.104138Z +b61604e25e4c9a96f362782071fd2f3e +2012-03-20T06:14:18.138919Z +58 +default8p@gmail.com +has-props + + + + + + + + + + + + + + + + + + + + +229888 + diff --git a/MySQL/.svn/prop-base/MySql.Data.Entity.dll.svn-base b/MySQL/.svn/prop-base/MySql.Data.Entity.dll.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/MySQL/.svn/prop-base/MySql.Data.Entity.dll.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/MySQL/.svn/prop-base/MySql.Data.dll.svn-base b/MySQL/.svn/prop-base/MySql.Data.dll.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/MySQL/.svn/prop-base/MySql.Data.dll.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/MySQL/.svn/prop-base/desktop.ini b/MySQL/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MySQL/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MySQL/.svn/props/desktop.ini b/MySQL/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/MySQL/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/MySQL/.svn/text-base/MySQL.cs.svn-base b/MySQL/.svn/text-base/MySQL.cs.svn-base new file mode 100644 index 0000000..75825f8 --- /dev/null +++ b/MySQL/.svn/text-base/MySQL.cs.svn-base @@ -0,0 +1,450 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MySql.Data.MySqlClient; +using ProxyCore; +using ProxyCore.Scripting; + +namespace MySQL +{ + public class MySQL : Plugin + { + public MySQL() + : base("mysql", "MySQL Database") + { + Author = "Duckbat"; + Version = 1; + Description = "Provides easy access to MySQL database."; + UpdateUrl = "www.duckbat.com/plugins/update.mysql.txt"; + Website = "code.google.com/p/proxymud/"; + + Config = new MySQLConfig(); + } + + internal static long MSTime; + private Database db; + + private void InitDB() + { + if(db == null) + db = new Database(Config.GetString("MySQL.Host", "localhost"), Config.GetString("MySQL.User", "root"), Config.GetString("MySQL.Pass", "pass"), Config.GetInt32("MySQL.Port", 3306), Config.GetString("MySQL.Database", "aardwolf")); + } + + public override void Shutdown() + { + base.Shutdown(); + + if(db != null) + { + db.CloseAll(); + db = null; + } + } + + public override void Update(long msTime) + { + base.Update(msTime); + + MSTime = msTime; + if(db != null) + db.Update(msTime); + } + + /// + /// Query data from the database. + /// + /// + /// + public QueryData Query(string q) + { + InitDB(); + return db.Query(q); + } + + /// + /// Execute a query in the database. + /// + /// + public void Execute(string q) + { + InitDB(); + db.Execute(q); + } + } + + public class MySQLConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("MySQL.Host", "localhost", "Hostname for MySQL connection."); + CreateSetting("MySQL.Port", 3306, "Port for MySQL connection."); + CreateSetting("MySQL.User", "root", "Username for MySQL connection."); + CreateSetting("MySQL.Pass", "pass", "Password for MySQL connection."); + CreateSetting("MySQL.Database", "aardwolf", "Database name for the MySQL connection."); + } + } + + internal class Database + { + internal Database(string Host, string Login, string Password, int Port, string dbName) + { + host = Host; + login = Login; + password = Password; + port = Port; + DBName = dbName; + } + + private readonly string host; + private readonly string login; + private readonly string password; + private readonly int port; + private readonly string DBName; + private Dictionary _con = new Dictionary(); + private MySqlConnection freeConnection = null; + private long freeTimer = 0; + + internal void Update(long msTime) + { + if(freeConnection != null && freeTimer < msTime) + { + freeConnection.Close(); + freeConnection = null; + } + + QueryData idle = null; + foreach(KeyValuePair x in _con) + { + if(x.Key.IsIdle) + { + idle = x.Key; + break; + } + } + + if(idle != null) + _CloseResult(idle); + } + + internal void CloseAll() + { + if(freeConnection != null) + { + freeConnection.Close(); + freeConnection = null; + } + + foreach(KeyValuePair x in _con) + { + x.Key._Close(); + x.Value.Close(); + } + + _con.Clear(); + } + + internal void _CloseResult(QueryData data) + { + data._Close(); + if(!_con.ContainsKey(data)) + return; + + if(freeConnection == null) + freeConnection = _con[data]; + _con.Remove(data); + freeTimer = MySQL.MSTime + 3000; + } + + internal QueryData Query(string q) + { + MySqlConnection con = null; + if(freeConnection == null) + { + string conString = "SERVER=" + host + ";DATABASE=" + DBName + ";UID=" + login + ";PASSWORD=" + password + ";PORT=" + port.ToString() + ";"; + con = new MySqlConnection(conString); + con.Open(); + } + else + { + con = freeConnection; + freeConnection = null; + } + + MySqlCommand cmd = con.CreateCommand(); + cmd.CommandText = q; + MySqlDataReader reader = cmd.ExecuteReader(); + + QueryData m = new QueryData(reader, this); + _con[m] = con; + return m; + } + + internal void Execute(string q) + { + MySqlConnection con = null; + if(freeConnection == null) + { + string conString = "SERVER=" + host + ";DATABASE=" + DBName + ";UID=" + login + ";PASSWORD=" + password + ";PORT=" + port.ToString() + ";"; + con = new MySqlConnection(conString); + con.Open(); + } + else + con = freeConnection; + + MySqlCommand cmd = con.CreateCommand(); + cmd.CommandText = q; + cmd.ExecuteNonQuery(); + if(freeConnection == null) + freeConnection = con; + else if(freeConnection != con) + con.Close(); + + freeTimer = MySQL.MSTime + 3000; + } + } + + public class QueryData + { + internal QueryData(MySqlDataReader res, Database connection) + { + r = res; + db = connection; + lastUpdate = MySQL.MSTime; + } + + internal readonly MySqlDataReader r; + internal readonly Database db; + + private long lastUpdate; + + internal bool IsIdle + { + get + { + return MySQL.MSTime - lastUpdate > 5000; + } + } + + /// + /// Close and free the result. + /// + public void Close() + { + db._CloseResult(this); + } + + internal void _Close() + { + if(!r.IsClosed) + r.Close(); + } + + /// + /// Read the next row in the result. If next row exists true will be returned, otherwise false. + /// + /// + public bool Read() + { + lastUpdate = MySQL.MSTime; + return r.Read(); + } + + /// + /// Read a double value in the specified field. Field index starts with 0. + /// + /// + /// + public double GetDouble(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetDouble(i); + return 0; + } + + /// + /// Read a float value in the specified field. Field index starts with 0. + /// + /// + /// + public float GetFloat(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetFloat(i); + return 0; + } + + /// + /// Read an integer value in the specified field. Field index starts with 0. + /// + /// + /// + public int GetInt32(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetInt32(i); + return 0; + } + + /// + /// Read an unsigned integer value in the specified field. Field index starts with 0. + /// + /// + /// + public uint GetUInt32(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetUInt32(i); + return 0; + } + + /// + /// Read a long value in the specified field. Field index starts with 0. + /// + /// + /// + public long GetInt64(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetInt64(i); + return 0; + } + + /// + /// Read an unsigned long value in the specified field. Field index starts with 0. + /// + /// + /// + public ulong GetUInt64(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetUInt64(i); + return 0; + } + + /// + /// Read a string value in the specified field. Field index starts with 0. + /// + /// + /// + public string GetString(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetString(i); + return null; + } + + /// + /// Read a short value in the specified field. Field index starts with 0. + /// + /// + /// + public short GetInt16(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetInt16(i); + return 0; + } + + /// + /// Read an unsigned short value in the specified field. Field index starts with 0. + /// + /// + /// + public ushort GetUInt16(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetUInt16(i); + return 0; + } + + /// + /// Read a byte value in the specified field. Field index starts with 0. + /// + /// + /// + public byte GetByte(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetByte(i); + return 0; + } + + /// + /// Read a char value in the specified field. Field index starts with 0. + /// + /// + /// + public char GetChar(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetChar(i); + return '\0'; + } + + /// + /// Read a boolean value in the specified field. Field index starts with 0. + /// + /// + /// + public bool GetBool(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetBoolean(i); + return false; + } + + /// + /// Read a date time value in the specified field. Field index starts with 0. + /// + /// + /// + public DateTime GetDateTime(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetDateTime(i); + return DateTime.MinValue; + } + + /// + /// Read a time span value in the specified field. Field index starts with 0. + /// + /// + /// + public TimeSpan GetTimeSpan(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetTimeSpan(i); + return TimeSpan.Zero; + } + + /// + /// Read an object value in the specified field. Field index starts with 0. + /// + /// + /// + public object GetValue(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetValue(i); + return 0; + } + } +} diff --git a/MySQL/.svn/text-base/MySQL.csproj.svn-base b/MySQL/.svn/text-base/MySQL.csproj.svn-base new file mode 100644 index 0000000..de10ac7 --- /dev/null +++ b/MySQL/.svn/text-base/MySQL.csproj.svn-base @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A0D29A29-FE11-4CB2-B256-B6EDD3B03A85} + Library + Properties + MySQL + MySQL + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + .\MySql.Data.dll + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/MySQL/.svn/text-base/MySql.Data.Entity.dll.svn-base b/MySQL/.svn/text-base/MySql.Data.Entity.dll.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..c9678e7f7072699be28d6ac10f953eb0a419db15 GIT binary patch literal 229888 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∓TjE9JGXdeQcq1@R9uiMt^|3?Z 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 Zo6a)+d4Ap + /// Query data from the database. + /// + /// + /// + public QueryData Query(string q) + { + InitDB(); + return db.Query(q); + } + + ///

+ /// Execute a query in the database. + /// + /// + public void Execute(string q) + { + InitDB(); + db.Execute(q); + } + } + + public class MySQLConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("MySQL.Host", "localhost", "Hostname for MySQL connection."); + CreateSetting("MySQL.Port", 3306, "Port for MySQL connection."); + CreateSetting("MySQL.User", "root", "Username for MySQL connection."); + CreateSetting("MySQL.Pass", "pass", "Password for MySQL connection."); + CreateSetting("MySQL.Database", "aardwolf", "Database name for the MySQL connection."); + } + } + + internal class Database + { + internal Database(string Host, string Login, string Password, int Port, string dbName) + { + host = Host; + login = Login; + password = Password; + port = Port; + DBName = dbName; + } + + private readonly string host; + private readonly string login; + private readonly string password; + private readonly int port; + private readonly string DBName; + private Dictionary _con = new Dictionary(); + private MySqlConnection freeConnection = null; + private long freeTimer = 0; + + internal void Update(long msTime) + { + if(freeConnection != null && freeTimer < msTime) + { + freeConnection.Close(); + freeConnection = null; + } + + QueryData idle = null; + foreach(KeyValuePair x in _con) + { + if(x.Key.IsIdle) + { + idle = x.Key; + break; + } + } + + if(idle != null) + _CloseResult(idle); + } + + internal void CloseAll() + { + if(freeConnection != null) + { + freeConnection.Close(); + freeConnection = null; + } + + foreach(KeyValuePair x in _con) + { + x.Key._Close(); + x.Value.Close(); + } + + _con.Clear(); + } + + internal void _CloseResult(QueryData data) + { + data._Close(); + if(!_con.ContainsKey(data)) + return; + + if(freeConnection == null) + freeConnection = _con[data]; + _con.Remove(data); + freeTimer = MySQL.MSTime + 3000; + } + + internal QueryData Query(string q) + { + MySqlConnection con = null; + if(freeConnection == null) + { + string conString = "SERVER=" + host + ";DATABASE=" + DBName + ";UID=" + login + ";PASSWORD=" + password + ";PORT=" + port.ToString() + ";"; + con = new MySqlConnection(conString); + con.Open(); + } + else + { + con = freeConnection; + freeConnection = null; + } + + MySqlCommand cmd = con.CreateCommand(); + cmd.CommandText = q; + MySqlDataReader reader = cmd.ExecuteReader(); + + QueryData m = new QueryData(reader, this); + _con[m] = con; + return m; + } + + internal void Execute(string q) + { + MySqlConnection con = null; + if(freeConnection == null) + { + string conString = "SERVER=" + host + ";DATABASE=" + DBName + ";UID=" + login + ";PASSWORD=" + password + ";PORT=" + port.ToString() + ";"; + con = new MySqlConnection(conString); + con.Open(); + } + else + con = freeConnection; + + MySqlCommand cmd = con.CreateCommand(); + cmd.CommandText = q; + cmd.ExecuteNonQuery(); + if(freeConnection == null) + freeConnection = con; + else if(freeConnection != con) + con.Close(); + + freeTimer = MySQL.MSTime + 3000; + } + } + + public class QueryData + { + internal QueryData(MySqlDataReader res, Database connection) + { + r = res; + db = connection; + lastUpdate = MySQL.MSTime; + } + + internal readonly MySqlDataReader r; + internal readonly Database db; + + private long lastUpdate; + + internal bool IsIdle + { + get + { + return MySQL.MSTime - lastUpdate > 5000; + } + } + + /// + /// Close and free the result. + /// + public void Close() + { + db._CloseResult(this); + } + + internal void _Close() + { + if(!r.IsClosed) + r.Close(); + } + + /// + /// Read the next row in the result. If next row exists true will be returned, otherwise false. + /// + /// + public bool Read() + { + lastUpdate = MySQL.MSTime; + return r.Read(); + } + + /// + /// Read a double value in the specified field. Field index starts with 0. + /// + /// + /// + public double GetDouble(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetDouble(i); + return 0; + } + + /// + /// Read a float value in the specified field. Field index starts with 0. + /// + /// + /// + public float GetFloat(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetFloat(i); + return 0; + } + + /// + /// Read an integer value in the specified field. Field index starts with 0. + /// + /// + /// + public int GetInt32(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetInt32(i); + return 0; + } + + /// + /// Read an unsigned integer value in the specified field. Field index starts with 0. + /// + /// + /// + public uint GetUInt32(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetUInt32(i); + return 0; + } + + /// + /// Read a long value in the specified field. Field index starts with 0. + /// + /// + /// + public long GetInt64(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetInt64(i); + return 0; + } + + /// + /// Read an unsigned long value in the specified field. Field index starts with 0. + /// + /// + /// + public ulong GetUInt64(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetUInt64(i); + return 0; + } + + /// + /// Read a string value in the specified field. Field index starts with 0. + /// + /// + /// + public string GetString(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetString(i); + return null; + } + + /// + /// Read a short value in the specified field. Field index starts with 0. + /// + /// + /// + public short GetInt16(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetInt16(i); + return 0; + } + + /// + /// Read an unsigned short value in the specified field. Field index starts with 0. + /// + /// + /// + public ushort GetUInt16(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetUInt16(i); + return 0; + } + + /// + /// Read a byte value in the specified field. Field index starts with 0. + /// + /// + /// + public byte GetByte(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetByte(i); + return 0; + } + + /// + /// Read a char value in the specified field. Field index starts with 0. + /// + /// + /// + public char GetChar(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetChar(i); + return '\0'; + } + + /// + /// Read a boolean value in the specified field. Field index starts with 0. + /// + /// + /// + public bool GetBool(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetBoolean(i); + return false; + } + + /// + /// Read a date time value in the specified field. Field index starts with 0. + /// + /// + /// + public DateTime GetDateTime(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetDateTime(i); + return DateTime.MinValue; + } + + /// + /// Read a time span value in the specified field. Field index starts with 0. + /// + /// + /// + public TimeSpan GetTimeSpan(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetTimeSpan(i); + return TimeSpan.Zero; + } + + /// + /// Read an object value in the specified field. Field index starts with 0. + /// + /// + /// + public object GetValue(int i) + { + lastUpdate = MySQL.MSTime; + if(r.FieldCount > i && !r.IsDBNull(i)) + return r.GetValue(i); + return 0; + } + } +} diff --git a/MySQL/MySQL.csproj b/MySQL/MySQL.csproj new file mode 100644 index 0000000..2e7e185 --- /dev/null +++ b/MySQL/MySQL.csproj @@ -0,0 +1,94 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A0D29A29-FE11-4CB2-B256-B6EDD3B03A85} + Library + Properties + MySQL + MySQL + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + TRACE + prompt + 4 + + + + False + .\MySql.Data.dll + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/MySQL/MySql.Data.Entity.dll b/MySQL/MySql.Data.Entity.dll new file mode 100644 index 0000000000000000000000000000000000000000..c9678e7f7072699be28d6ac10f953eb0a419db15 GIT binary patch literal 229888 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∓TjE9JGXdeQcq1@R9uiMt^|3?Z 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 Zo6a)+d4Ap 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 literal 0 HcmV?d00001 diff --git a/ProxyCore.xml b/ProxyCore.xml new file mode 100644 index 0000000..e1e6625 --- /dev/null +++ b/ProxyCore.xml @@ -0,0 +1,915 @@ + + + + ProxyCore + + + +

+ Load a configuration file. + + Configuration name - for example "server". This should be your plugin name. + + + + + Regex pattern used to load a line from config file. + Groups[1]: Name of setting + Groups[3]: Value of setting (including "" if string) + + + + + Save a new configuration file with default values. If there is an existing config file, it will be replaced. + + + + + This will be called to populate config data with default values and descriptions. + + + + + Create a new setting for the file. If setting with this name already exists then skip. + + Setting name. + Default value for setting. Make sure to use type casting if not integer. + Description to write in the file for this setting. + + + + Read a 32 bit integer value from configuration file. + + Name of the option. + Default value if config is missing this option or is invalid. + + + + + Read a 32 bit unsigned integer value from configuration file. + + Name of the option. + Default value if config is missing this option or is invalid. + + + + + Read a 64 bit integer value from configuration file. + + Name of the option. + Default value if config is missing this option or is invalid. + + + + + Read a 64 bit unsigned integer value from configuration file. + + Name of the option. + Default value if config is missing this option or is invalid. + + + + + Read a float value from configuration file. + + Name of the option. + Default value if config is missing this option or is invalid. + + + + + Read a double value from configuration file. + + Name of the option. + Default value if config is missing this option or is invalid. + + + + + Read a string value from configuration file. + + Name of the option. + Default value if config is missing this option or is invalid. + + + + + Did we successfully load the config file? If not then it was probably missing. + + + + + This is what we turn the @ into so we can replace colors and keep the symbol intact + + + + + Check if a char code is ANSI color code. + + Char to check for. + + + + + Convert normal colors to HTML colors. It converts inserting </font> in front of every color too + so you need to have started with some font already. + + Message to convert colors in. + + + + + Get last color code in the string (not including foreground colors). This will return + with the @ sign. + + + + + + + Removes duplicate colors from a string. For example "@r @Y bla@w" would become " @Ybla". + Also XTERM color codes become padded with zero if they are present. + + String to fix colors in. + + + + + This function will turn either ANSI color to our format or vice versa. + + String passed for color changing. + False means we change our format (@x) to ANSI; true means we change ANSI to our format (we don't convert XTERM colors this way though). + Allow replacing xterm colors? If disabled we will replace into closest ANSI. + + + + + Remove dark grey color code from string and replace it with normal grey. + + String to remove dark grey from (replacing it with normal grey). + + + + + This function will Remove all color from the string. + + String where the color is being removed. + True means raw color (ansi); false means our format (@x). + + + + + Get XTerm color code from text in index (index must be at @x or @z, before it). + + Text to get color code from. + Index of @z or @x. + + + + + Get full escape string from string in index. + + Text to search from. + Index in text of @e + Length of the thing behind @e. + + + + + Get XTerm color code from text in index (index must be at the number not before @x or @z like the other function) + + Text to get color code from. + Index of number. + Length of the number. + + + + + Replace XTerm color codes into real XTerm or ANSI values. + + Text to search in. + Replace into XTerm codes or if false then convert to closest regular ANSI. + + + + + Convert XTerm to ansi char (closest match). + + Code to convert. + + + + + Name of your plugin. You must set this or your plugin will not be loaded. + + + + + This is the keyword for your plugin. You must set this or your plugin will not be loaded. + This must be unique. If two or more plugins with the same keyword are found then the plugin + with the highest version number is loaded. + + + + + Register a new trigger. + + Unique identifier for the trigger. + Regex pattern for the trigger. + Function that will be called if this trigger fires. + + + + Register a new trigger. + + Unique identifier for the trigger. + Regex pattern for the trigger. + Function that will be called if this trigger fires. + Options for the trigger. + + + + Register a new trigger. + + Unique identifier for the trigger. + Regex pattern for the trigger. + Function that will be called if this trigger fires. + Options for the trigger. + Lower priority triggers get matched first. + + + + Register a new trigger. + + Unique identifier for the trigger. + Regex pattern for the trigger. + Function that will be called if this trigger fires. + Options for the trigger. + Lower priority triggers get matched first. + Custom argument that will be passed to trigger data. + + + + Unregister a trigger by name. + + Name of the trigger you wish to unregister. + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + Options for command. + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + Options for command. + Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + Options for command. + Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + Custom argument to pass to function handler. This way you can register multiple commands to a same + function handler only differentiating them with this custom argument. + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + Options for command. + Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + Custom argument to pass to function handler. This way you can register multiple commands to a same + function handler only differentiating them with this custom argument. + Mask of allowed auth levels to access this command. Default ulong.MaxValue (meaning all auth levels are allowed). + Enter 3 for example to allow only auth level 1 and 2 to access this command. + Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + + + + Unregister a command. + + Command to unregister. If you want to unregister a nested command + separate commands with a space. + + + + This will be called when character enters the game. Either by log in or reconnect. + + + + + This will be called when we disconnect from Aardwolf. + + + + + This will be called when we connect to Aardwolf. + + + + + This is called when program shuts down. Write any code you need to shut down your plugin. + + + + + This is called on every loop of world update. You can use it as your main loop for + the plugin if you need one. + + Current time since program startup. + + + + This is called when we receive a line from MUD. It is called AFTER triggers are done with it. If + a trigger gagged the line this will not be called. + + + + + + This is called when we receive a line from MUD. It is called BEFORE triggers are done with it. + + + + + + This is called when user enters a command and inputhandler did not handle the command. So it + is called AFTER we check for aliases and commands and we are about to send command to MUD. + + Command that was entered. You can change this in the function. If you set null + then nothing will be sent to MUD. + Client who entered the command. If this is 0 it was executed from a plugin. + Auth level of who entered the command. + + + + This is called when user enters a command. It is called BEFORE we check for aliases and commands. + + Command that was entered. You can change this in the function. If you set null + then nothing will be sent to MUD and nothing will be checked for aliases or commands. + Client who entered the command. If this is 0 it was executed from a plugin. + Auth level of who entered the command. + + + + Enter required player config options here. This will be displayed if user requests info about a plugin. + For example you may enter here "echocommands ON" and "statmon ON" etc. Whatever your plugin requires. + This doesn't actually change the settings in game it is only for plugin info command. + + + + + This is the class name of script. For example moons has this set to "MoonScript.MoonScript". + Only needed by developers who want to use another plugin in their plugin. + + + + + Called when we load a configuration file. + + Did the loading succeed? If not then the config file wasn't present and we created a new one. + + + + Disable all triggers with this priority. Disabling triggers from a plugin will make them not work until + you enable them from the same plugin again. If triggers have been disabled from multiple plugins then + all plugins will have to enable them again until they start working. Disabling triggers will make all + triggers with this priority to not work not only triggers in current plugin! + + Priority of triggers to disable. + + + + Disable all triggers with this priority. Disabling triggers from a plugin will make them not work until + you enable them from the same plugin again. If triggers have been disabled from multiple plugins then + all plugins will have to enable them again until they start working. Disabling triggers will make all + triggers with this priority to not work not only triggers in current plugin! + + Minimum priority of triggers to disable. + Maximum priority of triggers to disable. + + + + Enable all previously disabled triggers with this priority. + + Priority of triggers to enable. + + + + Enable all previously disabled triggers with this priority. + + Minimum priority of triggers to enable. + Maximum priority of triggers to enable. + + + + Set this to be your configuration file if you want your plugin to have one. This is optional. + + + + + Creator of the plugin, this is your name / character's name. This is optional. + + + + + Version of your plugin. This is optional. + + + + + Description about your plugin. You should explain here what it does and how to handle it. + This will be displayed if user requests information about your plugin. This is optional. + + + + + Enter a website for this plugin if you wish. Mostly used to see documentation and updates. + + + + + Enter the URL for update checking txt file. For example "www.duckbat.com/plugins/update.moons.txt". + In the text file enter the number of last version. For example whole contents of the txt file can be "3". + Indicating that the last version for this plugin is 3. If version is greater than user's version and + they have update checking on then they will be notified that there is a more up to date version out there. + + + + + Does this plugin require a certain version of core? Set this if there was an update and your plugin requires it. + Plugin is not loaded if an older version core than this is used and user will be notified. + + + + + Version of Proxy. + + + + + Handle text line as if we received it from Aardwolf. + + + + + + Handle GMCP data that we received from Aardwolf. + + GMCP data received. + + + + This is called from proxy when it shuts down. Do NOT call from a plugin. + + + + + Enter input as if a client entered it. Meaning we parse it. Consider using the Execute command instead. + + Input entered. + Which client is this from? Enter 0 to set not from a client. + Authlevel of client who entered command (1...64) + + + + Send message to specified clients. + + Message to send. + Clients to send it to. Enter null to send to all connected clients. + Enter 0 as client to send it as a command to Aardwolf (we don't parse it though, if you want + parsed input use the Execute command). + + + + Send a message to all connected authorized clients. + + Message to send to all authorized clients. + + + + Send a message to all connected authorized clients. + + Message to send to all authorized clients. + Authorization levels required to see this message. This is a mask. + + + + Execute a command. + + Command to execute. + Allow parsing it for aliases and such, or send it directly to Aardwolf? + + + + Execute a command. + + Command to execute. + Allow parsing it for aliases and such, or send it directly to Aardwolf? + Auth level that executes this command. (1...64) + + + + Send raw bytes to MUD. + + Bytes to send. + + + + Internal command for updating the world. DO NOT CALL FROM A PLUGIN! + + New mstime. + + + + This is messages for networking to handle. Don't touch unless you know what you are doing. + + + + + Check for updates and report to all connected users if there are any. + + Check core update. + Check plugin updates. + Should we report if no updates were found? + + + + Game world instance. + + + + + Milliseconds since program startup. + + + + + Get plugin by keyword. + + Keyword of plugin. + + + + + What we triggered on, you can change this value to replace the text. + + + + + Custom argument if you registered a trigger to have one. + + + + + Replace matched %0 value with new string. This only works in regex triggers. You can have {%1} - {%n} in the new string for matched data. + Use {%%1} to escape. If you use % higher than what was captured then a NULL will be replaced there. + For example using %3 when there are only 2 things captured with regex. + + Example: + Line: "@mQuest Points @w: @Y19,361" + Pattern: "^@mQuest Points @w: (\s*)@Y([\d,]+)$" + Replace: "@mQuest Points @w: {%1}@G{%2}" + This would make quest points green in the "worth" command output. Where {%1} inserts the right amount + of spaces (what was captured) and {%2} inserts the quest points amount (19,361). + + New string to replace with. See function summary for help with this. + Allow parsing {%n} in the New string or not. If not then string + is replaced as is without parsing for arguments. + + + + Match data. + + + + + This is the function template that will be called when a trigger fires. Return true to gag it - this + will also prevent other triggers from triggering on this line. + + Triggered text data. + + + + + Normal triggers stop after finding the first match in a line, + with this flag the trigger repeats for each match in the same line. + This only applies if you use regex pattern. + + + + + Ignore lower and upper case. This only applies if you use regex pattern. + + + + + Pattern is not regex but instead just raw string. For example if you want + to trigger on "@w--> @WTICK @w<--" there's no need to create a regex pattern + just insert this string and set this flag in options and the trigger will be MUCH + faster. Use regex only where necessary. + + + + + Trigger ignores color codes. If you set this flag you should NOT include any color codes + in your trigger pattern. + + + + + Matching will start from right and go to left. + + + + + Register a new trigger. + + Unique identifier for the trigger. + Regex pattern for the trigger. + Function that will be called if this trigger fires. + + + + Register a new trigger. + + Unique identifier for the trigger. + Regex pattern for the trigger. + Function that will be called if this trigger fires. + Options for the trigger. + + + + Register a new trigger. + + Unique identifier for the trigger. + Regex pattern for the trigger. + Function that will be called if this trigger fires. + Options for the trigger. + Lower priority triggers get matched first. Default: 1000 + + + + Register a new trigger. + + Unique identifier for the trigger. + Regex pattern for the trigger. + Function that will be called if this trigger fires. + Options for the trigger. + Lower priority triggers get matched first. Default: 1000 + Custom argument to pass to trigger data. + From which plugin was this registered. + + + + Unregister a trigger by name. + + Name of the trigger you wish to unregister. + + + + Function for handling input. Return true if we handled the command (no need to send to MUD) and false + if we didn't and we must send it to MUD. + + Input data. + + + + + Hidden from normal commands menu. + + + + + Command is currently disabled and will be excluded in the list of valid commands. + + + + + Dummy command, player can't type it but it will be redirected from elsewhere. + + + + + Internal - this will be assigned automatically for commands that have subcommands. + + + + + If this is set ignore message and send this byte data instead. Color codes + will not be parsed by server and this is sent as is. + + + + + This is message but without colors. Used for triggering non-ansi triggers. + + + + + Which clients should receive the message. This is a mask for security levels. + For example value "3" would only send this message to security level 1 and 2. + Set ulong.MaxValue (default) to send to all clients or 0 to send to noone. + This field is ignored when sending message to Aardwolf (as a command). + + + + + This setting is used to send the message to specific clients (using client ID). + Enter new uint[] { 0 } to send to Aardwolf and null to send to all clients (default). + + + + + Is this message natural (client entered command or Aardwolf sent message) or + is it generated by us, meaning Aardwolf did not send this and client did not enter + it as a command. + + + + + What kind of line ending do we send with this message (default \n\r). + + + + + When was message generated (send time may vary by some milliseconds) + + + + + Options for message. + + + + + Contents of the message. + + + + + This is a GMCP message the main module will be in Msg and the data will be in MsgData. + + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + Options for command. + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + Options for command. + Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + Options for command. + Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + Custom argument to pass to function handler. This way you can register multiple commands to a same + function handler only differentiating them with this custom argument. + + + + Register a new command or overwrite a previous one. + + Command to register. + Arguments to match (regex pattern). This can be set to null or empty string + if you don't want to capture anything or plan to do so in the function yourself. + Function of command. + Options for command. + Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + Custom argument to pass to function handler. This way you can register multiple commands to a same + function handler only differentiating them with this custom argument. + Mask of allowed auth levels to access this command. Default ulong.MaxValue (meaning all auth levels are allowed). + Enter 3 for example to allow only auth level 1 and 2 to access this command. + From which plugin did this come. + Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. + + + + Unregister a command. + + Command to unregister. If you want to unregister a nested command + separate commands with a space. + + + + Whole command just as it was entered. You can change this to send something different to MUD. Just make + sure you return false on the command handler otherwise nothing will get sent to MUD. + + + + + Which function will we execute with this data. + + + + + Who executed the command? This will be uint.MaxValue if we execute it from a plugin or other places - meaning + it didn't originate from a client. + + + + + Use this if you want to send message to whoever executed the command. If it was executed from a plugin + send message to every client. Example: World.Instance.SendMessage("Test.", i.ClientMask); + + + + + The auth level of client who entered command. (1...64) + + + + + This is where we capture arguments if the arguments pattern was set. Check first if Arguments.Success, otherwise there will be no Groups set. + + + + + Write this into console window. + + Message to write to console window. + + + + Write an error message to console window and error.log + + + + + + Write a stacktrace when program crashes. + + Trace to write. + + + diff --git a/ProxyCore/.svn/all-wcprops b/ProxyCore/.svn/all-wcprops new file mode 100644 index 0000000..b7e93bb --- /dev/null +++ b/ProxyCore/.svn/all-wcprops @@ -0,0 +1,23 @@ +K 25 +svn:wc:ra_dav:version-url +V 32 +/svn/!svn/ver/57/trunk/ProxyCore +END +Log.cs +K 25 +svn:wc:ra_dav:version-url +V 39 +/svn/!svn/ver/54/trunk/ProxyCore/Log.cs +END +ProxyCore.csproj +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2/trunk/ProxyCore/ProxyCore.csproj +END +World.cs +K 25 +svn:wc:ra_dav:version-url +V 41 +/svn/!svn/ver/57/trunk/ProxyCore/World.cs +END diff --git a/ProxyCore/.svn/desktop.ini b/ProxyCore/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/.svn/dir-prop-base b/ProxyCore/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/ProxyCore/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/ProxyCore/.svn/entries b/ProxyCore/.svn/entries new file mode 100644 index 0000000..3a6b974 --- /dev/null +++ b/ProxyCore/.svn/entries @@ -0,0 +1,148 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/ProxyCore +http://proxymud.googlecode.com/svn + + + +2012-02-08T08:41:07.654540Z +57 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +Utility +dir + +Log.cs +file + + + + +2016-03-25T22:18:43.228138Z +45e2f897ae1679bf333ba7bb710a266f +2012-02-02T08:38:33.263906Z +54 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1981 + +Messages +dir + +ProxyCore.csproj +file + + + + +2016-03-25T22:18:43.228138Z +2c65edfe3f4e32d352f9499841a632b7 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +3693 + +World.cs +file + + + + +2016-03-25T22:18:43.228138Z +4c194ca6a3b32f0eec02d527216faf32 +2012-02-08T08:41:07.654540Z +57 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +24207 + +Properties +dir + +Input +dir + +Output +dir + +Scripting +dir + diff --git a/ProxyCore/.svn/prop-base/desktop.ini b/ProxyCore/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/.svn/props/desktop.ini b/ProxyCore/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/.svn/text-base/Log.cs.svn-base b/ProxyCore/.svn/text-base/Log.cs.svn-base new file mode 100644 index 0000000..23382e4 --- /dev/null +++ b/ProxyCore/.svn/text-base/Log.cs.svn-base @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace ProxyCore +{ + public static class Log + { + /// + /// Write this into console window. + /// + /// Message to write to console window. + public static void Write(string msg) + { + Console.WriteLine(string.Format("[" + "{0:D2}" + ":" + "{1:D2}" + ":" + "{2:D2}" + "] ", DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second) + msg); + } + + /// + /// Write an error message to console window and error.log + /// + /// + public static void Error(string msg) + { + Write(msg); + try + { + StreamWriter f = new StreamWriter("error.log", true); + f.WriteLine(string.Format("[" + "{0:D2}" + ":" + "{1:D2}" + ":" + "{2:D2}" + "] ", DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second) + msg); + f.Close(); + } + catch + { + } + } + + /// + /// Write a stacktrace when program crashes. + /// + /// Trace to write. + public static void Crash(Exception e, string Module) + { + string[] E = e.StackTrace.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + World.Instance.SendMessage("@RCRASH: Proxy crashed in module \"@w" + Module + "@R\"! Check error.log for details and send stack trace to author of module."); + Error("Program crashed in module (" + Module + ") with exception: \"" + e.Message + "\", type: \"" + e.GetType().ToString() + "\", trace:"); + foreach(string x in E) + Error(x); + Error("Trace end."); + } + } +} diff --git a/ProxyCore/.svn/text-base/ProxyCore.csproj.svn-base b/ProxyCore/.svn/text-base/ProxyCore.csproj.svn-base new file mode 100644 index 0000000..8bb0e26 --- /dev/null +++ b/ProxyCore/.svn/text-base/ProxyCore.csproj.svn-base @@ -0,0 +1,84 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + Library + Properties + ProxyCore + ProxyCore + v3.5 + 512 + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + bin\ProxyCore.XML + + + pdbonly + true + bin\ + TRACE + prompt + 4 + bin\ProxyCore.XML + + + + False + ..\Resources\Jayrock.dll + + + False + ..\Resources\Jayrock.Json.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ProxyCore/.svn/text-base/World.cs.svn-base b/ProxyCore/.svn/text-base/World.cs.svn-base new file mode 100644 index 0000000..b04b185 --- /dev/null +++ b/ProxyCore/.svn/text-base/World.cs.svn-base @@ -0,0 +1,647 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore.Input; +using ProxyCore.Output; +using ProxyCore.Messages; +using ProxyCore.Scripting; +using System.Net; +using System.IO; +using System.Text.RegularExpressions; + +namespace ProxyCore +{ + public class World + { + public World() + { + Instance = this; + Log.Write("Loading plugins..."); + PluginMgr.LoadAll(); + TriggerHandler.RegisterTrigger("core.login", @"^\$gmcp\.char\.vitals\.hp ", _GMCPHP); + InputHandler.RegisterCommand("commands", "", _Commands, CMDFlags.None, null, 0, ulong.MaxValue, "core", 8); + InputHandler.RegisterCommand("lastlines", @"^(\d+)$", _LineInfo, CMDFlags.None, null, 0, ulong.MaxValue, "core", 8); + InputHandler.RegisterCommand("plugins", @"^(\w+)(\s+full)?", _PluginInfo, CMDFlags.None, null, 0, ulong.MaxValue, "core", 6); + InputHandler.RegisterCommand("shutdown", "", _Shutdown, CMDFlags.None, null, 0, ulong.MaxValue, "core", 8); + } + + private bool _Shutdown(InputData i) + { + _doShutdown = true; + return true; + } + + private bool _Commands(InputData i) + { + SendMessage("@wCommands in @Wcore@w:", i.ClientMask); + int c = WriteCommands(InputHandler.Commands, "core", i.ClientMask); + SendMessage("@C" + c + " @wcommand" + (c == 1 ? "" : "s") + " found."); + + foreach(KeyValuePair p in PluginMgr.Plugins) + { + SendMessage("", i.ClientMask); + SendMessage("@wCommands in @W" + p.Key + "@w:", i.ClientMask); + c = WriteCommands(InputHandler.Commands, p.Key, i.ClientMask); + SendMessage("@C" + c + " @wcommand" + (c == 1 ? "" : "s") + " found."); + } + + SendMessage("", i.ClientMask); + SendMessage("@WThese are commands for the proxy. If you want to see MUD commands type command.", i.ClientMask); + return true; + } + + private int WriteCommands(SortedDictionary y, string plugin, uint[] clientMask) + { + if(y == null || y.Count == 0) + return 0; + + int c = 0; + foreach(KeyValuePair x in y) + { + if(x.Value.Plugin != plugin) + continue; + if((x.Value.Flags & (CMDFlags.Disabled | CMDFlags.Hidden)) != CMDFlags.None) + continue; + string cmd = ""; + InputEntry p = x.Value.Parent; + while(p != null) + { + cmd = cmd.Insert(0, p.Command + " "); + p = p.Parent; + } + SendMessage("@Y" + cmd, clientMask); + c++; + + c += WriteCommands(x.Value.Subcommands, plugin, clientMask); + } + + return c; + } + + /// + /// Game world instance. + /// + public static World Instance + { + get; + private set; + } + + /// + /// Handle text line as if we received it from Aardwolf. + /// + /// + public void _OnReceived(string Msg) + { + TriggerHandler.HandleText(Msg, this); + } + + public void _OnConnected(bool connected) + { + isWorldReady = false; + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + if(connected) + x.Value.OnConnect(); + else + x.Value.OnDisconnect(); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + } + } + + private bool _GMCPHP(TriggerData t) + { + if(!isWorldReady) + { + isWorldReady = true; + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.OnLogin(); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + } + CheckUpdatesNow = MSTime + 5000; + } + return false; + } + + private long CheckUpdatesNow = 0; + + private bool _LineInfo(InputData i) + { + int j = 10; + if(i.Arguments.Success) + { + if(!int.TryParse(i.Arguments.Groups[1].Value, out j)) + j = 10; + } + + if(j > 100) + j = 100; + else if(j < 1) + j = 1; + + SendMessage("@wDisplaying last @Y" + j + " @wline" + (j != 1 ? "s" : "") + ":", i.ClientMask); + j = lastLine.Count - j; + if(j < 0) + j = 0; + for(; j < lastLine.Count; j++) + SendMessage(lastLine[j].Replace("@", "@@"), i.ClientMask); + if(j == 10) + SendMessage("@wType '@Wlastlines @w' to see amount of lines.", i.ClientMask); + return true; + } + + internal List lastLine = new List(); + + private bool _PluginInfo(InputData i) + { + if(!i.Arguments.Success) + { + SendMessage("@wYou have the following plugins installed:", i.ClientMask); + foreach(KeyValuePair x in PluginMgr.Plugins) + SendMessage("@Y" + string.Format("{0,-20}", x.Value.Keyword.Trim()) + " @w- " + x.Value.Name + ", version " + x.Value.Version.ToString(), i.ClientMask); + if(PluginMgr.Plugins.Count == 0) + SendMessage("@RYou have no plugins installed.", i.ClientMask); + else + { + SendMessage("", i.ClientMask); + SendMessage("@C" + PluginMgr.Plugins.Count + " @wplugin" + (PluginMgr.Plugins.Count != 1 ? "s" : "") + " found."); + SendMessage("@wUse '@Wplugin @w' for more information about a plugin.", i.ClientMask); + SendMessage("@wUse '@Wplugin full@w' for all information about a plugin.", i.ClientMask); + //SendMessage("@wUse '@Wplugin write@w' to write all information about plugin to a file.", i.ClientMask); + } + } + else + { + Plugin p = PluginMgr.Plugins.ContainsKey(i.Arguments.Groups[1].Value.ToLower().Trim()) ? PluginMgr.Plugins[i.Arguments.Groups[1].Value.ToLower().Trim()] : null; + if(p == null) + { + SendMessage("@wNo such plugin (@W" + i.Arguments.Groups[1].Value.ToLower().Trim() + "@w).", i.ClientMask); + SendMessage("@wType '@Wplugins@w' for a list of installed plugins.", i.ClientMask); + return true; + } + + SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + SendMessage("@w|@R" + p.Name.Substring(0, p.Name.Length / 2).PadLeft(35, ' ') + + p.Name.Substring(p.Name.Length / 2).PadRight(35, ' ') + "@w|", i.ClientMask); + SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + SendMessage("@w| @WKeyword @w: @g" + string.Format("{0,-55}", p.Keyword) + "@w|", i.ClientMask); + SendMessage("@w| @WAuthor @w: @g" + string.Format("{0,-55}", !string.IsNullOrEmpty(p.Author) ? p.Author : "Unknown") + "@w|", i.ClientMask); + SendMessage("@w| @WVersion @w: @Y" + string.Format("{0,-55}", p.Version) + "@w|", i.ClientMask); + if(!string.IsNullOrEmpty(p.Website)) + SendMessage("@w| @WWebsite @w: @Y" + string.Format("{0,-55}", p.Website) + "@w|", i.ClientMask); + SendMessage("@w| @WClass name @w: @g" + string.Format("{0,-55}", p.ClassName) + "@w|", i.ClientMask); + if(!string.IsNullOrEmpty(p.Description)) + { + string[] desc = Utility.WrapColored(p.Description, 54, 0); + for(int j = 0; j < desc.Length; j++) + { + SendMessage( + j == 0 + ? ("@w| @WDescription @w: @C" + string.Format("{0,-55}", desc[j]) + "@w|") + : ("@w| : @C" + string.Format("{0,-55}", desc[j]) + "@w|"), i.ClientMask); + } + } + if(p.RequiredPlayerConfig.Count != 0) + { + for(int j = 0; j < p.RequiredPlayerConfig.Count; j++) + { + SendMessage( + j == 0 + ? ("@w| @WReq. config @w: " + string.Format("{0,-55}", p.RequiredPlayerConfig[j]) + + "@w|") + : ("@w| : " + string.Format("{0,-55}", p.RequiredPlayerConfig[j]) + "@w|"), + i.ClientMask); + } + } + SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + if(i.Arguments.Groups[2].Length > 0 || i.Arguments.Groups[3].Length > 0) + { + int j = _PluginInfoWriteCommands(0, InputHandler.Commands, p.Keyword.ToLower().Trim(), i.ClientMask); + + int k = 0; + foreach(KeyValuePair x in TriggerHandler.TriggersName) + { + if(x.Value.Plugin != p.Keyword.ToLower().Trim()) + continue; + + if(k == 0) + { + SendMessage( + "@w| @WTriggers @w: \"" + + Utility.FormatColoredString(x.Value.PatternStr.Replace("@", "@@") + "\"", -54) + "@w|", + i.ClientMask); + } + else + { + SendMessage( + "@w| : \"" + + Utility.FormatColoredString(x.Value.PatternStr.Replace("@", "@@") + "\"", -54) + "@w|", + i.ClientMask); + } + k++; + } + + if(j != 0 || k != 0) + { + SendMessage("@w+----------------------------------------------------------------------+", + i.ClientMask); + } + } + } + + return true; + } + + private int _PluginInfoWriteCommands(int j, SortedDictionary y, string plugin, uint[] clientMask) + { + foreach(KeyValuePair x in y) + { + if(x.Value.Plugin != plugin) + continue; + + string cmd = x.Key; + InputEntry parent = x.Value.Parent; + while(parent != null) + { + cmd = cmd.Insert(0, parent.Command + " "); + parent = parent.Parent; + } + + if(j == 0) + SendMessage("@w| @WCommands @w: @c" + string.Format("{0,-55}", cmd) + "@w|", clientMask); + else + SendMessage("@w| : @c" + string.Format("{0,-55}", cmd) + "@w|", clientMask); + j++; + + if(x.Value.Subcommands != null) + j = _PluginInfoWriteCommands(j, x.Value.Subcommands, plugin, clientMask); + } + + return j; + } + + private bool isWorldReady = false; + + /// + /// Handle GMCP data that we received from Aardwolf. + /// + /// GMCP data received. + public void _OnReceived(byte[] Data) + { + string Msg = Encoding.Default.GetString(Data); + string Module = Msg.Trim().ToLower(); + if(Module.Contains(' ')) + Module = Module.Substring(0, Module.IndexOf(' ')); + Message m = new Message(true); + m.Clients = null; + m.Flags |= MessageFlags.GMCP; + m.Msg = Module; + m.MsgData = Data; + _SendMessage(m); + TriggerHandler.HandleGMCP(Msg); + } + + /// + /// This is called from proxy when it shuts down. Do NOT call from a plugin. + /// + public void Shutdown() + { + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.Shutdown(); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + } + + _doShutdown = true; + } + + /// + /// Enter input as if a client entered it. Meaning we parse it. Consider using the Execute command instead. + /// + /// Input entered. + /// Which client is this from? Enter 0 to set not from a client. + /// Authlevel of client who entered command (1...64) + public void _OnSent(string Msg, uint ClientId, int AuthLevel) + { + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.OnEnteredCommandBefore(ref Msg, ClientId, AuthLevel); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + if(Msg == null) + return; + } + + InputData i = InputHandler.HandleInput(Msg, Msg, ClientId, AuthLevel, null, this); + if(i != null) + { +#if !DEBUG + try + { +#endif + if(i.Function.Func(i)) + return; +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, i.Function.Plugin); + } +#endif + Msg = i.Command; + } + + if(Msg != null) + { + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.OnEnteredCommandAfter(ref Msg, ClientId, AuthLevel); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Value.Keyword); + } +#endif + if(Msg == null) + return; + } + _SendMessage(Msg, new uint[] {0}, ClientId != 0); + } + } + + internal void _SendMessage(string Msg, uint[] Clients, bool Natural) + { + _SendMessage(Msg, Clients, Natural, MessageFlags.None); + } + + internal void _SendMessage(string Msg, uint[] Clients, bool Natural, MessageFlags Flags) + { + _SendMessage(Msg, Clients, Natural, Flags, ulong.MaxValue); + } + + internal void _SendMessage(string Msg, uint[] Clients, bool Natural, MessageFlags Flags, ulong AuthMask) + { + Message m = new Message(Natural); + m.Clients = Clients; + m.Msg = Msg; + m.Flags = Flags; + m.AuthMask = AuthMask; + _SendMessage(m); + } + + internal void _SendMessage(Message msg) + { + _MessageData.Add(msg); + } + + /// + /// Send message to specified clients. + /// + /// Message to send. + /// Clients to send it to. Enter null to send to all connected clients. + /// Enter 0 as client to send it as a command to Aardwolf (we don't parse it though, if you want + /// parsed input use the Execute command). + public void SendMessage(string Msg, uint[] Clients) + { + _SendMessage(Msg, Clients, false); + } + + /// + /// Send a message to all connected authorized clients. + /// + /// Message to send to all authorized clients. + public void SendMessage(string Msg) + { + SendMessage(Msg, null); + } + + /// + /// Send a message to all connected authorized clients. + /// + /// Message to send to all authorized clients. + /// Authorization levels required to see this message. This is a mask. + public void SendMessage(string Msg, ulong AuthMask) + { + _SendMessage(Msg, null, false, MessageFlags.None, AuthMask); + } + + /// + /// Execute a command. + /// + /// Command to execute. + /// Allow parsing it for aliases and such, or send it directly to Aardwolf? + public void Execute(string Msg, bool allowParse) + { + Execute(Msg, allowParse, 1); + } + + /// + /// Execute a command. + /// + /// Command to execute. + /// Allow parsing it for aliases and such, or send it directly to Aardwolf? + /// Auth level that executes this command. (1...64) + public void Execute(string Msg, bool allowParse, int AuthLevel) + { + if(Msg == null) + return; + + if(allowParse) + _OnSent(Msg, uint.MaxValue, Math.Max(1, Math.Min(64, AuthLevel))); + else + _SendMessage(Msg, new uint[] { 0 }, false); + } + + /// + /// Send raw bytes to MUD. + /// + /// Bytes to send. + public void Send(byte[] Data) + { + if(Data == null) + return; + + Message m = new Message(false); + m.Clients = new uint[] { 0 }; + m.MsgData = Data; + _SendMessage(m); + } + + /// + /// Internal command for updating the world. DO NOT CALL FROM A PLUGIN! + /// + /// New mstime. + public bool Update(long msTime) + { + MSTime = msTime; + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.Update(msTime); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + } + + if(CheckUpdatesNow != 0 && msTime > CheckUpdatesNow) + { + CheckUpdatesNow = 0; + CheckUpdates(Config.GetInt32("Updates.Core", 1) != 0, Config.GetInt32("Updates.Plugins", 1) != 0, false); + } + + return _doShutdown; + } + + private bool _doShutdown = false; + + /// + /// This is messages for networking to handle. Don't touch unless you know what you are doing. + /// + public readonly List _MessageData = new List(256); + + internal CoreConfig Config = new CoreConfig(); + + /// + /// Version of Proxy. + /// + public const int Version = 7; + public const string CoreUrl = "www.duckbat.com/plugins/update.core.txt"; + public const string CoreUrl2 = "code.google.com/p/proxymud/"; + + private int GetVersion(string Url) + { + if(!_urlRegex.Match(Url).Success) + Url = "http://" + Url; + WebClient w = new WebClient(); + byte[] d = w.DownloadData(Url); + string s = Encoding.Default.GetString(d); + s = s.Replace("\r", ""); + s = s.Replace(" ", ""); + s = s.Replace("\n", ""); + return int.Parse(s); + } + + private static Regex _urlRegex = new Regex(@"^\w+://", RegexOptions.Compiled); + + /// + /// Milliseconds since program startup. + /// + public long MSTime + { + get; + private set; + } + + /// + /// Check for updates and report to all connected users if there are any. + /// + /// Check core update. + /// Check plugin updates. + /// Should we report if no updates were found? + public void CheckUpdates(bool Core, bool Plugins, bool ReportNoUpdates) + { +#if DEBUG + return; +#endif + bool s = false; + if(Core) + { + try + { + int coreVer = GetVersion(CoreUrl); + if(coreVer > Version) + { + SendMessage("@GUPDATE: @wThere is a newer version of @Wcore @wavailable. (@W" + coreVer + "@w)"); + SendMessage(" @wGo to @W" + CoreUrl2 + " @wto see more."); + s = true; + } + } + catch + { + } + } + + if(Plugins) + { + foreach(KeyValuePair x in PluginMgr.Plugins) + { + if(string.IsNullOrEmpty(x.Value.Website) || string.IsNullOrEmpty(x.Value.UpdateUrl)) + continue; + + try + { + int ver = GetVersion(x.Value.UpdateUrl); + if(ver > x.Value.Version) + { + if(s) + SendMessage(""); + SendMessage("@GUPDATE: @wThere is a newer version of @W" + x.Value.Keyword.ToLower().Trim() + " @wavailable. (@W" + ver + "@w)"); + SendMessage(" @wGo to @W" + x.Value.Website + " @wto see more."); + s = true; + } + } + catch + { + continue; + } + } + } + + if(ReportNoUpdates && !s) + SendMessage("@GUPDATE: @wNo updates found."); + } + } +} diff --git a/ProxyCore/.svn/text-base/desktop.ini b/ProxyCore/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/.svn/tmp/desktop.ini b/ProxyCore/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/.svn/tmp/prop-base/desktop.ini b/ProxyCore/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/.svn/tmp/props/desktop.ini b/ProxyCore/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/.svn/tmp/text-base/desktop.ini b/ProxyCore/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Input/.svn/all-wcprops b/ProxyCore/Input/.svn/all-wcprops new file mode 100644 index 0000000..b1976c9 --- /dev/null +++ b/ProxyCore/Input/.svn/all-wcprops @@ -0,0 +1,23 @@ +K 25 +svn:wc:ra_dav:version-url +V 37 +/svn/!svn/ver/2/trunk/ProxyCore/Input +END +InputData.cs +K 25 +svn:wc:ra_dav:version-url +V 50 +/svn/!svn/ver/2/trunk/ProxyCore/Input/InputData.cs +END +InputHandler.cs +K 25 +svn:wc:ra_dav:version-url +V 53 +/svn/!svn/ver/2/trunk/ProxyCore/Input/InputHandler.cs +END +InputEntry.cs +K 25 +svn:wc:ra_dav:version-url +V 51 +/svn/!svn/ver/2/trunk/ProxyCore/Input/InputEntry.cs +END diff --git a/ProxyCore/Input/.svn/desktop.ini b/ProxyCore/Input/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Input/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Input/.svn/dir-prop-base b/ProxyCore/Input/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/ProxyCore/Input/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/ProxyCore/Input/.svn/entries b/ProxyCore/Input/.svn/entries new file mode 100644 index 0000000..552b024 --- /dev/null +++ b/ProxyCore/Input/.svn/entries @@ -0,0 +1,130 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/ProxyCore/Input +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +InputData.cs +file + + + + +2016-03-25T22:18:43.184138Z +155a373f27748daa21a7fce96108399b +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1995 + +InputHandler.cs +file + + + + +2016-03-25T22:18:43.184138Z +560caee9040d3d6c238a177cf5050917 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +12480 + +InputEntry.cs +file + + + + +2016-03-25T22:18:43.184138Z +cf1a6e8a4d7f6acb5e29469c2042ac0d +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1617 + diff --git a/ProxyCore/Input/.svn/prop-base/desktop.ini b/ProxyCore/Input/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Input/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Input/.svn/props/desktop.ini b/ProxyCore/Input/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Input/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Input/.svn/text-base/InputData.cs.svn-base b/ProxyCore/Input/.svn/text-base/InputData.cs.svn-base new file mode 100644 index 0000000..4293b57 --- /dev/null +++ b/ProxyCore/Input/.svn/text-base/InputData.cs.svn-base @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace ProxyCore.Input +{ + public class InputData + { + internal InputData() + { + } + + /// + /// Who executed the command? This will be uint.MaxValue if we execute it from a plugin or other places - meaning + /// it didn't originate from a client. + /// + public uint ClientId + { + get; + internal set; + } + + /// + /// Use this if you want to send message to whoever executed the command. If it was executed from a plugin + /// send message to every client. Example: World.Instance.SendMessage("Test.", i.ClientMask); + /// + public uint[] ClientMask + { + get + { + return ClientId != uint.MaxValue ? new[] { ClientId } : null; + } + } + + /// + /// The auth level of client who entered command. (1...64) + /// + public int ClientAuthLevel + { + get; + internal set; + } + + /// + /// Whole command just as it was entered. You can change this to send something different to MUD. Just make + /// sure you return false on the command handler otherwise nothing will get sent to MUD. + /// + public string Command; + + /// + /// Which function will we execute with this data. + /// + internal InputEntry Function; + + /// + /// This is where we capture arguments if the arguments pattern was set. Check first if Arguments.Success, otherwise there will be no Groups set. + /// + public Match Arguments + { + get; + internal set; + } + } +} diff --git a/ProxyCore/Input/.svn/text-base/InputEntry.cs.svn-base b/ProxyCore/Input/.svn/text-base/InputEntry.cs.svn-base new file mode 100644 index 0000000..10dda3f --- /dev/null +++ b/ProxyCore/Input/.svn/text-base/InputEntry.cs.svn-base @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace ProxyCore.Input +{ + internal class InputEntry + { + internal string Command; + internal CmdFunction Func; + internal CMDFlags Flags; + internal InputEntry Parent; + internal int CustomArg; + internal Regex ArgumentsPattern; + internal ulong AuthMask; + internal string Plugin; + internal int MinLength; + internal SortedDictionary Subcommands; + } + + /// + /// Function for handling input. Return true if we handled the command (no need to send to MUD) and false + /// if we didn't and we must send it to MUD. + /// + /// Input data. + /// + public delegate bool CmdFunction(InputData cmd); + + [Flags] + public enum CMDFlags + { + None = 0, + + /// + /// Hidden from normal commands menu. + /// + Hidden = 2, + + /// + /// Command is currently disabled and will be excluded in the list of valid commands. + /// + Disabled = 4, + + /// + /// Dummy command, player can't type it but it will be redirected from elsewhere. + /// + Dummy = 8, + + /// + /// Internal - this will be assigned automatically for commands that have subcommands. + /// + IsParent = 0x10, + } +} diff --git a/ProxyCore/Input/.svn/text-base/InputHandler.cs.svn-base b/ProxyCore/Input/.svn/text-base/InputHandler.cs.svn-base new file mode 100644 index 0000000..931c6d8 --- /dev/null +++ b/ProxyCore/Input/.svn/text-base/InputHandler.cs.svn-base @@ -0,0 +1,285 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace ProxyCore.Input +{ + internal static class InputHandler + { + static InputHandler() + { + } + + internal static SortedDictionary Commands = new SortedDictionary(); + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + internal static void RegisterCommand(string Cmd, string Args, CmdFunction f) + { + RegisterCommand(Cmd, Args, f, CMDFlags.None); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Options for command. + internal static void RegisterCommand(string Cmd, string Args, CmdFunction f, CMDFlags flags) + { + RegisterCommand(Cmd, Args, f, flags, null); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + internal static void RegisterCommand(string Cmd, string Args, CmdFunction f, CMDFlags flags, string parent) + { + RegisterCommand(Cmd, Args, f, flags, parent, 0); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + /// Custom argument to pass to function handler. This way you can register multiple commands to a same + /// function handler only differentiating them with this custom argument. + internal static void RegisterCommand(string Cmd, string Args, CmdFunction f, CMDFlags flags, string parent, int Arg) + { + RegisterCommand(Cmd, Args, f, flags, parent, Arg, ulong.MaxValue, "core", 0); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + /// Custom argument to pass to function handler. This way you can register multiple commands to a same + /// function handler only differentiating them with this custom argument. + /// Mask of allowed auth levels to access this command. Default ulong.MaxValue (meaning all auth levels are allowed). + /// Enter 3 for example to allow only auth level 1 and 2 to access this command. + /// From which plugin did this come. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. + internal static void RegisterCommand(string Cmd, string Args, CmdFunction f, CMDFlags flags, string parent, int Arg, ulong AuthMask, string Plugin, int ReqMinLength) + { + if(string.IsNullOrEmpty(Cmd)) + return; + + Cmd = Cmd.ToLower().Trim(); + if(string.IsNullOrEmpty(Cmd)) + return; + + if(Cmd.Contains(' ')) + Cmd = Cmd.Substring(0, Cmd.IndexOf(' ')); + + InputEntry p = null; + if(!string.IsNullOrEmpty(parent)) + { + string[] pc = parent.ToLower().Trim().Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); + if(pc.Length == 0) + return; + + if(Commands.ContainsKey(pc[0])) + { + p = Commands[pc[0]]; + for(int i = 1; i < pc.Length; i++) + { + if(p.Subcommands != null && p.Subcommands.ContainsKey(pc[i])) + p = p.Subcommands[pc[i]]; + else + return; + } + } + else + return; + } + + InputEntry c = new InputEntry() + { + Command = Cmd, + CustomArg = Arg, + Flags = flags, + Func = f, + Parent = p, + Plugin = Plugin, + MinLength = ReqMinLength, + AuthMask = AuthMask + }; + + try + { + c.ArgumentsPattern = new Regex(Args, RegexOptions.IgnoreCase); + } + catch + { + c.ArgumentsPattern = null; + } + + if(p != null) + { + if(p.Subcommands == null) + p.Subcommands = new SortedDictionary(); + p.Subcommands[Cmd] = c; + p.Flags |= CMDFlags.IsParent; + } + else + Commands[Cmd] = c; + } + + /// + /// Unregister a command. + /// + /// Command to unregister. If you want to unregister a nested command + /// separate commands with a space. + internal static void UnregisterCommand(string Cmd) + { + if(Cmd == null) + return; + + string[] pc = Cmd.ToLower().Trim().Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); + if(pc.Length == 0) + return; + + if(!Commands.ContainsKey(pc[0])) + return; + + if(pc.Length > 1) + { + InputEntry p = Commands[pc[0]]; + for(int i = 1; i < pc.Length; i++) + { + if(p.Subcommands == null || !p.Subcommands.ContainsKey(pc[i])) + return; + p = p.Subcommands[pc[i]]; + } + + p.Parent.Subcommands.Remove(p.Command); + if(p.Parent.Subcommands.Count == 0) + p.Parent.Flags &= ~CMDFlags.IsParent; + } + else + Commands.Remove(pc[0]); + } + + internal static InputData HandleInput(string origCommand, string Msg, uint ClientId, int AuthLevel, InputEntry parent, World world) + { + Msg = Msg.Trim(); + string cmd = ""; + string text = ""; + + if(Msg.Contains(' ')) + { + cmd = Msg.Substring(0, Msg.IndexOf(' ')); + text = Msg.Substring(Msg.IndexOf(' ') + 1).Trim(); + } + else + { + cmd = Msg; + } + + cmd = cmd.ToLower(); + InputEntry f = null; + + if(!string.IsNullOrEmpty(cmd)) + { + if(parent == null) + { + foreach(KeyValuePair x in Commands) + { + if(x.Key == cmd && (x.Value.Flags & (CMDFlags.Dummy | CMDFlags.Disabled)) == CMDFlags.None && (x.Value.AuthMask & ((ulong)1 << (AuthLevel - 1))) != 0) + { + f = x.Value; + break; + } + } + + if(f == null) + { + foreach(KeyValuePair x in Commands) + { + if(x.Value.MinLength <= 0) + continue; + if(cmd.Length < x.Value.Command.Length && cmd.Length >= x.Value.MinLength && x.Value.Command.StartsWith(cmd) && (x.Value.Flags & (CMDFlags.Dummy | CMDFlags.Disabled)) == CMDFlags.None && (x.Value.AuthMask & ((ulong)1 << (AuthLevel - 1))) != 0) + { + f = x.Value; + break; + } + } + } + } + else if(parent.Subcommands != null) + { + foreach(KeyValuePair x in parent.Subcommands) + { + if(x.Key == cmd && (x.Value.Flags & (CMDFlags.Dummy | CMDFlags.Disabled)) == CMDFlags.None && (x.Value.AuthMask & ((ulong)1 << (AuthLevel - 1))) != 0) + { + f = x.Value; + break; + } + } + + if(f == null) + { + foreach(KeyValuePair x in parent.Subcommands) + { + if(x.Value.MinLength <= 0) + continue; + if(cmd.Length < x.Value.Command.Length && cmd.Length >= x.Value.MinLength && x.Value.Command.StartsWith(cmd) && (x.Value.Flags & (CMDFlags.Dummy | CMDFlags.Disabled)) == CMDFlags.None && (x.Value.AuthMask & ((ulong)1 << (AuthLevel - 1))) != 0) + { + f = x.Value; + break; + } + } + } + } + } + else + { + if(parent != null && parent.Func != null) + f = parent; + else + return null; + } + + if(f != null && (f.Flags & CMDFlags.IsParent) != CMDFlags.None && !string.IsNullOrEmpty(text)) + return HandleInput(origCommand, text, ClientId, AuthLevel, f, world); + + if(f == null || f.Func == null) + return null; + + InputData data = new InputData(); + data.Command = origCommand; + data.ClientId = ClientId; + data.ClientAuthLevel = AuthLevel; + data.Function = f; + if(f.ArgumentsPattern != null) + data.Arguments = f.ArgumentsPattern.Match(text); + + return data; + } + } +} diff --git a/ProxyCore/Input/.svn/text-base/desktop.ini b/ProxyCore/Input/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Input/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Input/.svn/tmp/desktop.ini b/ProxyCore/Input/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Input/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Input/.svn/tmp/prop-base/desktop.ini b/ProxyCore/Input/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Input/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Input/.svn/tmp/props/desktop.ini b/ProxyCore/Input/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Input/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Input/.svn/tmp/text-base/desktop.ini b/ProxyCore/Input/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Input/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Input/InputData.cs b/ProxyCore/Input/InputData.cs new file mode 100644 index 0000000..4293b57 --- /dev/null +++ b/ProxyCore/Input/InputData.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace ProxyCore.Input +{ + public class InputData + { + internal InputData() + { + } + + /// + /// Who executed the command? This will be uint.MaxValue if we execute it from a plugin or other places - meaning + /// it didn't originate from a client. + /// + public uint ClientId + { + get; + internal set; + } + + /// + /// Use this if you want to send message to whoever executed the command. If it was executed from a plugin + /// send message to every client. Example: World.Instance.SendMessage("Test.", i.ClientMask); + /// + public uint[] ClientMask + { + get + { + return ClientId != uint.MaxValue ? new[] { ClientId } : null; + } + } + + /// + /// The auth level of client who entered command. (1...64) + /// + public int ClientAuthLevel + { + get; + internal set; + } + + /// + /// Whole command just as it was entered. You can change this to send something different to MUD. Just make + /// sure you return false on the command handler otherwise nothing will get sent to MUD. + /// + public string Command; + + /// + /// Which function will we execute with this data. + /// + internal InputEntry Function; + + /// + /// This is where we capture arguments if the arguments pattern was set. Check first if Arguments.Success, otherwise there will be no Groups set. + /// + public Match Arguments + { + get; + internal set; + } + } +} diff --git a/ProxyCore/Input/InputEntry.cs b/ProxyCore/Input/InputEntry.cs new file mode 100644 index 0000000..10dda3f --- /dev/null +++ b/ProxyCore/Input/InputEntry.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace ProxyCore.Input +{ + internal class InputEntry + { + internal string Command; + internal CmdFunction Func; + internal CMDFlags Flags; + internal InputEntry Parent; + internal int CustomArg; + internal Regex ArgumentsPattern; + internal ulong AuthMask; + internal string Plugin; + internal int MinLength; + internal SortedDictionary Subcommands; + } + + /// + /// Function for handling input. Return true if we handled the command (no need to send to MUD) and false + /// if we didn't and we must send it to MUD. + /// + /// Input data. + /// + public delegate bool CmdFunction(InputData cmd); + + [Flags] + public enum CMDFlags + { + None = 0, + + /// + /// Hidden from normal commands menu. + /// + Hidden = 2, + + /// + /// Command is currently disabled and will be excluded in the list of valid commands. + /// + Disabled = 4, + + /// + /// Dummy command, player can't type it but it will be redirected from elsewhere. + /// + Dummy = 8, + + /// + /// Internal - this will be assigned automatically for commands that have subcommands. + /// + IsParent = 0x10, + } +} diff --git a/ProxyCore/Input/InputHandler.cs b/ProxyCore/Input/InputHandler.cs new file mode 100644 index 0000000..931c6d8 --- /dev/null +++ b/ProxyCore/Input/InputHandler.cs @@ -0,0 +1,285 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace ProxyCore.Input +{ + internal static class InputHandler + { + static InputHandler() + { + } + + internal static SortedDictionary Commands = new SortedDictionary(); + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + internal static void RegisterCommand(string Cmd, string Args, CmdFunction f) + { + RegisterCommand(Cmd, Args, f, CMDFlags.None); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Options for command. + internal static void RegisterCommand(string Cmd, string Args, CmdFunction f, CMDFlags flags) + { + RegisterCommand(Cmd, Args, f, flags, null); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + internal static void RegisterCommand(string Cmd, string Args, CmdFunction f, CMDFlags flags, string parent) + { + RegisterCommand(Cmd, Args, f, flags, parent, 0); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + /// Custom argument to pass to function handler. This way you can register multiple commands to a same + /// function handler only differentiating them with this custom argument. + internal static void RegisterCommand(string Cmd, string Args, CmdFunction f, CMDFlags flags, string parent, int Arg) + { + RegisterCommand(Cmd, Args, f, flags, parent, Arg, ulong.MaxValue, "core", 0); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + /// Custom argument to pass to function handler. This way you can register multiple commands to a same + /// function handler only differentiating them with this custom argument. + /// Mask of allowed auth levels to access this command. Default ulong.MaxValue (meaning all auth levels are allowed). + /// Enter 3 for example to allow only auth level 1 and 2 to access this command. + /// From which plugin did this come. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. + internal static void RegisterCommand(string Cmd, string Args, CmdFunction f, CMDFlags flags, string parent, int Arg, ulong AuthMask, string Plugin, int ReqMinLength) + { + if(string.IsNullOrEmpty(Cmd)) + return; + + Cmd = Cmd.ToLower().Trim(); + if(string.IsNullOrEmpty(Cmd)) + return; + + if(Cmd.Contains(' ')) + Cmd = Cmd.Substring(0, Cmd.IndexOf(' ')); + + InputEntry p = null; + if(!string.IsNullOrEmpty(parent)) + { + string[] pc = parent.ToLower().Trim().Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); + if(pc.Length == 0) + return; + + if(Commands.ContainsKey(pc[0])) + { + p = Commands[pc[0]]; + for(int i = 1; i < pc.Length; i++) + { + if(p.Subcommands != null && p.Subcommands.ContainsKey(pc[i])) + p = p.Subcommands[pc[i]]; + else + return; + } + } + else + return; + } + + InputEntry c = new InputEntry() + { + Command = Cmd, + CustomArg = Arg, + Flags = flags, + Func = f, + Parent = p, + Plugin = Plugin, + MinLength = ReqMinLength, + AuthMask = AuthMask + }; + + try + { + c.ArgumentsPattern = new Regex(Args, RegexOptions.IgnoreCase); + } + catch + { + c.ArgumentsPattern = null; + } + + if(p != null) + { + if(p.Subcommands == null) + p.Subcommands = new SortedDictionary(); + p.Subcommands[Cmd] = c; + p.Flags |= CMDFlags.IsParent; + } + else + Commands[Cmd] = c; + } + + /// + /// Unregister a command. + /// + /// Command to unregister. If you want to unregister a nested command + /// separate commands with a space. + internal static void UnregisterCommand(string Cmd) + { + if(Cmd == null) + return; + + string[] pc = Cmd.ToLower().Trim().Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); + if(pc.Length == 0) + return; + + if(!Commands.ContainsKey(pc[0])) + return; + + if(pc.Length > 1) + { + InputEntry p = Commands[pc[0]]; + for(int i = 1; i < pc.Length; i++) + { + if(p.Subcommands == null || !p.Subcommands.ContainsKey(pc[i])) + return; + p = p.Subcommands[pc[i]]; + } + + p.Parent.Subcommands.Remove(p.Command); + if(p.Parent.Subcommands.Count == 0) + p.Parent.Flags &= ~CMDFlags.IsParent; + } + else + Commands.Remove(pc[0]); + } + + internal static InputData HandleInput(string origCommand, string Msg, uint ClientId, int AuthLevel, InputEntry parent, World world) + { + Msg = Msg.Trim(); + string cmd = ""; + string text = ""; + + if(Msg.Contains(' ')) + { + cmd = Msg.Substring(0, Msg.IndexOf(' ')); + text = Msg.Substring(Msg.IndexOf(' ') + 1).Trim(); + } + else + { + cmd = Msg; + } + + cmd = cmd.ToLower(); + InputEntry f = null; + + if(!string.IsNullOrEmpty(cmd)) + { + if(parent == null) + { + foreach(KeyValuePair x in Commands) + { + if(x.Key == cmd && (x.Value.Flags & (CMDFlags.Dummy | CMDFlags.Disabled)) == CMDFlags.None && (x.Value.AuthMask & ((ulong)1 << (AuthLevel - 1))) != 0) + { + f = x.Value; + break; + } + } + + if(f == null) + { + foreach(KeyValuePair x in Commands) + { + if(x.Value.MinLength <= 0) + continue; + if(cmd.Length < x.Value.Command.Length && cmd.Length >= x.Value.MinLength && x.Value.Command.StartsWith(cmd) && (x.Value.Flags & (CMDFlags.Dummy | CMDFlags.Disabled)) == CMDFlags.None && (x.Value.AuthMask & ((ulong)1 << (AuthLevel - 1))) != 0) + { + f = x.Value; + break; + } + } + } + } + else if(parent.Subcommands != null) + { + foreach(KeyValuePair x in parent.Subcommands) + { + if(x.Key == cmd && (x.Value.Flags & (CMDFlags.Dummy | CMDFlags.Disabled)) == CMDFlags.None && (x.Value.AuthMask & ((ulong)1 << (AuthLevel - 1))) != 0) + { + f = x.Value; + break; + } + } + + if(f == null) + { + foreach(KeyValuePair x in parent.Subcommands) + { + if(x.Value.MinLength <= 0) + continue; + if(cmd.Length < x.Value.Command.Length && cmd.Length >= x.Value.MinLength && x.Value.Command.StartsWith(cmd) && (x.Value.Flags & (CMDFlags.Dummy | CMDFlags.Disabled)) == CMDFlags.None && (x.Value.AuthMask & ((ulong)1 << (AuthLevel - 1))) != 0) + { + f = x.Value; + break; + } + } + } + } + } + else + { + if(parent != null && parent.Func != null) + f = parent; + else + return null; + } + + if(f != null && (f.Flags & CMDFlags.IsParent) != CMDFlags.None && !string.IsNullOrEmpty(text)) + return HandleInput(origCommand, text, ClientId, AuthLevel, f, world); + + if(f == null || f.Func == null) + return null; + + InputData data = new InputData(); + data.Command = origCommand; + data.ClientId = ClientId; + data.ClientAuthLevel = AuthLevel; + data.Function = f; + if(f.ArgumentsPattern != null) + data.Arguments = f.ArgumentsPattern.Match(text); + + return data; + } + } +} diff --git a/ProxyCore/Input/desktop.ini b/ProxyCore/Input/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Input/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Log.cs b/ProxyCore/Log.cs new file mode 100644 index 0000000..23382e4 --- /dev/null +++ b/ProxyCore/Log.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace ProxyCore +{ + public static class Log + { + /// + /// Write this into console window. + /// + /// Message to write to console window. + public static void Write(string msg) + { + Console.WriteLine(string.Format("[" + "{0:D2}" + ":" + "{1:D2}" + ":" + "{2:D2}" + "] ", DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second) + msg); + } + + /// + /// Write an error message to console window and error.log + /// + /// + public static void Error(string msg) + { + Write(msg); + try + { + StreamWriter f = new StreamWriter("error.log", true); + f.WriteLine(string.Format("[" + "{0:D2}" + ":" + "{1:D2}" + ":" + "{2:D2}" + "] ", DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second) + msg); + f.Close(); + } + catch + { + } + } + + /// + /// Write a stacktrace when program crashes. + /// + /// Trace to write. + public static void Crash(Exception e, string Module) + { + string[] E = e.StackTrace.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + World.Instance.SendMessage("@RCRASH: Proxy crashed in module \"@w" + Module + "@R\"! Check error.log for details and send stack trace to author of module."); + Error("Program crashed in module (" + Module + ") with exception: \"" + e.Message + "\", type: \"" + e.GetType().ToString() + "\", trace:"); + foreach(string x in E) + Error(x); + Error("Trace end."); + } + } +} diff --git a/ProxyCore/Messages/.svn/all-wcprops b/ProxyCore/Messages/.svn/all-wcprops new file mode 100644 index 0000000..49abc09 --- /dev/null +++ b/ProxyCore/Messages/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 40 +/svn/!svn/ver/2/trunk/ProxyCore/Messages +END +Message.cs +K 25 +svn:wc:ra_dav:version-url +V 51 +/svn/!svn/ver/2/trunk/ProxyCore/Messages/Message.cs +END diff --git a/ProxyCore/Messages/.svn/desktop.ini b/ProxyCore/Messages/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Messages/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Messages/.svn/dir-prop-base b/ProxyCore/Messages/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/ProxyCore/Messages/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/ProxyCore/Messages/.svn/entries b/ProxyCore/Messages/.svn/entries new file mode 100644 index 0000000..47a0ef5 --- /dev/null +++ b/ProxyCore/Messages/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/ProxyCore/Messages +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +Message.cs +file + + + + +2016-03-25T22:18:43.216138Z +875894e5c9ff74a2fad723d3170d09c9 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2923 + diff --git a/ProxyCore/Messages/.svn/prop-base/desktop.ini b/ProxyCore/Messages/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Messages/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Messages/.svn/props/desktop.ini b/ProxyCore/Messages/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Messages/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Messages/.svn/text-base/Message.cs.svn-base b/ProxyCore/Messages/.svn/text-base/Message.cs.svn-base new file mode 100644 index 0000000..49d33f4 --- /dev/null +++ b/ProxyCore/Messages/.svn/text-base/Message.cs.svn-base @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ProxyCore.Messages +{ + public class Message + { + internal Message(bool isNaturalMessage) + { + IsNaturalMessage = isNaturalMessage; + } + + /// + /// Contents of the message. + /// + public string Msg + { + get + { + return _msg; + } + set + { + _msg = value; + MsgNoColor = _msg == null ? null : Colors.RemoveColors(_msg, false); + } + } + + private string _msg; + + /// + /// If this is set ignore message and send this byte data instead. Color codes + /// will not be parsed by server and this is sent as is. + /// + public byte[] MsgData = null; + + /// + /// This is message but without colors. Used for triggering non-ansi triggers. + /// + internal string MsgNoColor = null; + + /// + /// Which clients should receive the message. This is a mask for security levels. + /// For example value "3" would only send this message to security level 1 and 2. + /// Set ulong.MaxValue (default) to send to all clients or 0 to send to noone. + /// This field is ignored when sending message to Aardwolf (as a command). + /// + public ulong AuthMask = ulong.MaxValue; + + /// + /// This setting is used to send the message to specific clients (using client ID). + /// Enter new uint[] { 0 } to send to Aardwolf and null to send to all clients (default). + /// + public uint[] Clients = null; + + /// + /// Is this message natural (client entered command or Aardwolf sent message) or + /// is it generated by us, meaning Aardwolf did not send this and client did not enter + /// it as a command. + /// + public readonly bool IsNaturalMessage; + + /// + /// What kind of line ending do we send with this message (default \n\r). + /// + public string LineEnding = "\n\r"; + + /// + /// When was message generated (send time may vary by some milliseconds) + /// + public readonly DateTime Timestamp = DateTime.Now; + + /// + /// Options for message. + /// + public MessageFlags Flags = MessageFlags.None; + } + + [Flags] + public enum MessageFlags + { + None = 0, + + /// + /// This is a GMCP message the main module will be in Msg and the data will be in MsgData. + /// + GMCP = 0x1, + } +} diff --git a/ProxyCore/Messages/.svn/text-base/desktop.ini b/ProxyCore/Messages/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Messages/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Messages/.svn/tmp/desktop.ini b/ProxyCore/Messages/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Messages/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Messages/.svn/tmp/prop-base/desktop.ini b/ProxyCore/Messages/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Messages/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Messages/.svn/tmp/props/desktop.ini b/ProxyCore/Messages/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Messages/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Messages/.svn/tmp/text-base/desktop.ini b/ProxyCore/Messages/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Messages/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Messages/Message.cs b/ProxyCore/Messages/Message.cs new file mode 100644 index 0000000..49d33f4 --- /dev/null +++ b/ProxyCore/Messages/Message.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ProxyCore.Messages +{ + public class Message + { + internal Message(bool isNaturalMessage) + { + IsNaturalMessage = isNaturalMessage; + } + + /// + /// Contents of the message. + /// + public string Msg + { + get + { + return _msg; + } + set + { + _msg = value; + MsgNoColor = _msg == null ? null : Colors.RemoveColors(_msg, false); + } + } + + private string _msg; + + /// + /// If this is set ignore message and send this byte data instead. Color codes + /// will not be parsed by server and this is sent as is. + /// + public byte[] MsgData = null; + + /// + /// This is message but without colors. Used for triggering non-ansi triggers. + /// + internal string MsgNoColor = null; + + /// + /// Which clients should receive the message. This is a mask for security levels. + /// For example value "3" would only send this message to security level 1 and 2. + /// Set ulong.MaxValue (default) to send to all clients or 0 to send to noone. + /// This field is ignored when sending message to Aardwolf (as a command). + /// + public ulong AuthMask = ulong.MaxValue; + + /// + /// This setting is used to send the message to specific clients (using client ID). + /// Enter new uint[] { 0 } to send to Aardwolf and null to send to all clients (default). + /// + public uint[] Clients = null; + + /// + /// Is this message natural (client entered command or Aardwolf sent message) or + /// is it generated by us, meaning Aardwolf did not send this and client did not enter + /// it as a command. + /// + public readonly bool IsNaturalMessage; + + /// + /// What kind of line ending do we send with this message (default \n\r). + /// + public string LineEnding = "\n\r"; + + /// + /// When was message generated (send time may vary by some milliseconds) + /// + public readonly DateTime Timestamp = DateTime.Now; + + /// + /// Options for message. + /// + public MessageFlags Flags = MessageFlags.None; + } + + [Flags] + public enum MessageFlags + { + None = 0, + + /// + /// This is a GMCP message the main module will be in Msg and the data will be in MsgData. + /// + GMCP = 0x1, + } +} diff --git a/ProxyCore/Messages/desktop.ini b/ProxyCore/Messages/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Messages/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Output/.svn/all-wcprops b/ProxyCore/Output/.svn/all-wcprops new file mode 100644 index 0000000..edd15e4 --- /dev/null +++ b/ProxyCore/Output/.svn/all-wcprops @@ -0,0 +1,23 @@ +K 25 +svn:wc:ra_dav:version-url +V 39 +/svn/!svn/ver/54/trunk/ProxyCore/Output +END +TriggerEntry.cs +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/!svn/ver/40/trunk/ProxyCore/Output/TriggerEntry.cs +END +TriggerData.cs +K 25 +svn:wc:ra_dav:version-url +V 53 +/svn/!svn/ver/2/trunk/ProxyCore/Output/TriggerData.cs +END +TriggerHandler.cs +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/!svn/ver/54/trunk/ProxyCore/Output/TriggerHandler.cs +END diff --git a/ProxyCore/Output/.svn/desktop.ini b/ProxyCore/Output/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Output/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Output/.svn/dir-prop-base b/ProxyCore/Output/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/ProxyCore/Output/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/ProxyCore/Output/.svn/entries b/ProxyCore/Output/.svn/entries new file mode 100644 index 0000000..4bf1932 --- /dev/null +++ b/ProxyCore/Output/.svn/entries @@ -0,0 +1,130 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/ProxyCore/Output +http://proxymud.googlecode.com/svn + + + +2012-02-02T08:38:33.263906Z +54 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +TriggerData.cs +file + + + + +2016-03-25T22:18:43.188138Z +0d3b660b908fd57e16d0fa2ac14f1fc1 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +3483 + +TriggerHandler.cs +file + + + + +2016-03-25T22:18:43.188138Z +2224e1c4aeda63d93aace92dc537037b +2012-02-02T08:38:33.263906Z +54 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +12669 + +TriggerEntry.cs +file + + + + +2016-03-25T22:18:43.188138Z +462f17031b8eb6511c5fefa7688f64f5 +2012-01-25T09:39:59.831649Z +40 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2179 + diff --git a/ProxyCore/Output/.svn/prop-base/desktop.ini b/ProxyCore/Output/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Output/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Output/.svn/props/desktop.ini b/ProxyCore/Output/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Output/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Output/.svn/text-base/TriggerData.cs.svn-base b/ProxyCore/Output/.svn/text-base/TriggerData.cs.svn-base new file mode 100644 index 0000000..8ca66b5 --- /dev/null +++ b/ProxyCore/Output/.svn/text-base/TriggerData.cs.svn-base @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using ProxyCore.Messages; + +namespace ProxyCore.Output +{ + public class TriggerData + { + internal TriggerData() + { + } + + /// + /// Match data. + /// + public Match Match + { + get; + internal set; + } + + /// + /// What we triggered on, you can change this value to replace the text. + /// + public Message Msg; + + /// + /// Custom argument if you registered a trigger to have one. + /// + public int Arg; + + /// + /// Replace matched %0 value with new string. This only works in regex triggers. You can have {%1} - {%n} in the new string for matched data. + /// Use {%%1} to escape. If you use % higher than what was captured then a NULL will be replaced there. + /// For example using %3 when there are only 2 things captured with regex. + /// + /// Example: + /// Line: "@mQuest Points @w: @Y19,361" + /// Pattern: "^@mQuest Points @w: (\s*)@Y([\d,]+)$" + /// Replace: "@mQuest Points @w: {%1}@G{%2}" + /// This would make quest points green in the "worth" command output. Where {%1} inserts the right amount + /// of spaces (what was captured) and {%2} inserts the quest points amount (19,361). + /// + /// New string to replace with. See function summary for help with this. + /// Allow parsing {%n} in the New string or not. If not then string + /// is replaced as is without parsing for arguments. + public void Replace(string New, bool AllowParse) + { + if(Match == null || !Match.Success) + return; + + if(AllowParse) + { + Match m; + if(New.Contains("{%")) // Make fast check first if we want to start messing with regex + { + while((m = _replaceRegex.Match(New)).Success) + { + int i; + if(!int.TryParse(m.Groups[1].Value, out i) || i >= Match.Groups.Count || i < 0) + New = New.Replace(m.Groups[0].Value, "NULL"); + else + New = New.Replace(m.Groups[0].Value, Match.Groups[i].Value); + } + } + if(New.Contains("{%%")) + { + while((m = _replaceRegex2.Match(New)).Success) + { + New = New.Replace(m.Groups[0].Value, "{%" + m.Groups[1].Value + "}"); + } + } + } + + // Don't use replace here because we only want to replace one occurance, wherever it was matched + Msg.Msg = Msg.Msg.Remove(Match.Index, Match.Length).Insert(Match.Index, New); + } + + private static readonly Regex _replaceRegex = new Regex(@"\{%(\d+)\}", RegexOptions.Compiled); + private static readonly Regex _replaceRegex2 = new Regex(@"\{%%(\d+)\}", RegexOptions.Compiled); + } +} diff --git a/ProxyCore/Output/.svn/text-base/TriggerEntry.cs.svn-base b/ProxyCore/Output/.svn/text-base/TriggerEntry.cs.svn-base new file mode 100644 index 0000000..76bfe6d --- /dev/null +++ b/ProxyCore/Output/.svn/text-base/TriggerEntry.cs.svn-base @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace ProxyCore.Output +{ + internal class TriggerEntry + { + internal Regex Pattern; + internal string PatternStr; + internal TriggerFunction Function; + internal int Priority; + internal string Name; + internal TriggerFlags Flags; + internal int Arg; + internal string Plugin; + internal List Disabled; + } + + /// + /// This is the function template that will be called when a trigger fires. Return true to gag it - this + /// will also prevent other triggers from triggering on this line. + /// + /// Triggered text data. + /// + public delegate bool TriggerFunction(TriggerData Data); + + [Flags] + public enum TriggerFlags + { + None = 0, + + /// + /// Normal triggers stop after finding the first match in a line, + /// with this flag the trigger repeats for each match in the same line. + /// This only applies if you use regex pattern. + /// + Repeat = 1, + + /// + /// Ignore lower and upper case. This only applies if you use regex pattern. + /// + CaseInsensitive = 2, + + /// + /// Pattern is not regex but instead just raw string. For example if you want + /// to trigger on "@w--> @WTICK @w<--" there's no need to create a regex pattern + /// just insert this string and set this flag in options and the trigger will be MUCH + /// faster. Use regex only where necessary. + /// + NotRegex = 4, + + /// + /// Trigger ignores color codes. If you set this flag you should NOT include any color codes + /// in your trigger pattern. + /// + NonAnsi = 8, + + /// + /// Matching will start from right and go to left. + /// + RightToLeft = 0x10, + } +} diff --git a/ProxyCore/Output/.svn/text-base/TriggerHandler.cs.svn-base b/ProxyCore/Output/.svn/text-base/TriggerHandler.cs.svn-base new file mode 100644 index 0000000..aa7af14 --- /dev/null +++ b/ProxyCore/Output/.svn/text-base/TriggerHandler.cs.svn-base @@ -0,0 +1,345 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using ProxyCore.Messages; +using ProxyCore.Scripting; + +namespace ProxyCore.Output +{ + internal static class TriggerHandler + { + static TriggerHandler() + { + } + + private static SortedDictionary> Triggers = new SortedDictionary>(); + internal static Dictionary TriggersName = new Dictionary(); + + internal static void DisableTriggers(string FromPlugin, int MinPriority, int MaxPriority) + { + if(FromPlugin == null) + return; + FromPlugin = FromPlugin.ToLower().Trim(); + if(FromPlugin.Length == 0) + return; + foreach(KeyValuePair> x in Triggers) + { + if(x.Key < MinPriority) + continue; + if(x.Key > MaxPriority) + break; + + foreach(TriggerEntry y in x.Value) + { + if(y.Disabled == null) + y.Disabled = new List(); + if(!y.Disabled.Contains(FromPlugin)) + y.Disabled.Add(FromPlugin); + } + } + } + + internal static void EnableTriggers(string FromPlugin, int MinPriority, int MaxPriority) + { + if(FromPlugin == null) + return; + FromPlugin = FromPlugin.ToLower().Trim(); + if(FromPlugin.Length == 0) + return; + foreach(KeyValuePair> x in Triggers) + { + if(x.Key < MinPriority) + continue; + if(x.Key > MaxPriority) + break; + + foreach(TriggerEntry y in x.Value) + { + if(y.Disabled == null) + continue; + y.Disabled.Remove(FromPlugin); + } + } + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + internal static void RegisterTrigger(string Name, string Pattern, TriggerFunction Function) + { + RegisterTrigger(Name, Pattern, Function, TriggerFlags.None); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + internal static void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags) + { + RegisterTrigger(Name, Pattern, Function, Flags, 1000); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + /// Lower priority triggers get matched first. Default: 1000 + internal static void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags, int Priority) + { + RegisterTrigger(Name, Pattern, Function, Flags, Priority, 0, "core"); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + /// Lower priority triggers get matched first. Default: 1000 + /// Custom argument to pass to trigger data. + /// From which plugin was this registered. + internal static void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags, int Priority, int Arg, string Plugin) + { + if(string.IsNullOrEmpty(Pattern) || string.IsNullOrEmpty(Name) || Function == null) + return; + + Name = Name.ToLower().Trim(); + if(Name.Length == 0) + return; + + Regex p = null; + if((Flags & TriggerFlags.NotRegex) == TriggerFlags.None) + { + try + { + RegexOptions op = RegexOptions.None; + if((Flags & TriggerFlags.RightToLeft) != TriggerFlags.None) + op |= RegexOptions.RightToLeft; + if((Flags & TriggerFlags.CaseInsensitive) != TriggerFlags.None) + op |= RegexOptions.IgnoreCase; + p = new Regex(Pattern, op); + } + catch + { + return; + } + } + + TriggerEntry e = new TriggerEntry(); + e.Function = Function; + e.Pattern = p; + e.PatternStr = Pattern; + e.Priority = Priority; + e.Name = Name; + e.Flags = Flags; + e.Arg = Arg; + e.Plugin = Plugin; + + if(TriggersName.ContainsKey(Name)) + Triggers[TriggersName[Name].Priority].Remove(TriggersName[Name]); + + TriggersName[Name] = e; + if(!Triggers.ContainsKey(e.Priority)) + Triggers[e.Priority] = new List(); + Triggers[e.Priority].Add(e); + } + + /// + /// Unregister a trigger by name. + /// + /// Name of the trigger you wish to unregister. + internal static void UnregisterTrigger(string Name) + { + if(Name != null) + Name = Name.ToLower().Trim(); + if(string.IsNullOrEmpty(Name)) + return; + + if(!TriggersName.ContainsKey(Name)) + return; + + Triggers[TriggersName[Name].Priority].Remove(TriggersName[Name]); + TriggersName.Remove(Name); + } + + internal static void HandleText(string Msg, World world) + { + if(!string.IsNullOrEmpty(lastColorCode)) + Msg = lastColorCode + Msg; + lastColorCode = Colors.GetLastColorCode(Msg); + Msg = Colors.RemoveDuplicateColors(Msg); + + Message m = new Message(true); + m.Msg = Msg; + HandleLineRaw(m); + if(m.Msg != null) + { + world._SendMessage(m); + world.lastLine.Add(m.Msg); + while(world.lastLine.Count > 100) + world.lastLine.RemoveAt(0); + } + } + + private static string lastColorCode = ""; + + private static readonly List> gmcpData = + new List>(); + + internal static void HandleGMCP(string Msg) + { + //string origMsg = Msg; + string module; + try + { + int ind = Msg.IndexOf(' '); + if(ind == -1) + { + module = Msg; + Msg = ""; + } + else + { + module = Msg.Substring(0, ind); + Msg = Msg.Substring(ind).Trim(); + } + } + catch + { + return; + } + + if(string.IsNullOrEmpty(module)) + return; + + if(gmcpData.Count != 0) + gmcpData.Clear(); + module = module.ToLower(); + bool res = JSON.Parse(Msg, module, gmcpData); + if(!res) + return; + + if(gmcpData.Count == 0) + HandleGMCP(module, null); + else + { + foreach(KeyValuePair i in gmcpData) + HandleGMCP(i.Key.ToLower().Trim(), i.Value); + } + } + + private static void HandleGMCP(string Module, string Value) + { + Message m = new Message(true); + m.Msg = "$gmcp." + Module + (Value != null ? (" " + Value) : ""); + HandleLineRaw(m); + } + + private static void HandleLineRaw(Message Msg) + { + foreach(KeyValuePair x in PluginMgr.Plugins) + { + try + { + x.Value.OnReceivedLineBefore(Msg); + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } + if(Msg.Msg == null) + return; + } + + foreach(KeyValuePair> x in Triggers) + { + foreach(TriggerEntry y in x.Value) + { + if(y.Disabled != null && y.Disabled.Count != 0) + continue; + + int i = 0; + while(Msg.Msg != null) + { + int o = i; + Match m = null; + if((y.Flags & TriggerFlags.NotRegex) == TriggerFlags.None) + { + if((y.Flags & TriggerFlags.RightToLeft) != TriggerFlags.None) + m = y.Pattern.Match((y.Flags & TriggerFlags.NonAnsi) == TriggerFlags.None ? Msg.Msg : Msg.MsgNoColor); + else + m = y.Pattern.Match((y.Flags & TriggerFlags.NonAnsi) == TriggerFlags.None ? Msg.Msg : Msg.MsgNoColor, i); + if(!m.Success) + break; + } + else if(y.PatternStr != ((y.Flags & TriggerFlags.NonAnsi) == TriggerFlags.None ? Msg.Msg : Msg.MsgNoColor)) + break; + + TriggerData d = new TriggerData(); + d.Match = m; + d.Arg = y.Arg; + d.Msg = Msg; +#if !DEBUG + try + { +#endif + if(y.Function(d)) + { + Msg.Msg = null; + return; + } +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, y.Plugin); + } +#endif + + if((y.Flags & TriggerFlags.NotRegex) != TriggerFlags.None) + break; + if((y.Flags & TriggerFlags.RightToLeft) != TriggerFlags.None) + break; + + i = m.Groups[0].Index + m.Groups[0].Length; + if((y.Flags & TriggerFlags.Repeat) == TriggerFlags.None) + break; + + if(i == o) + i++; + } + } + } + + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.OnReceivedLineAfter(Msg); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + if(Msg.Msg == null) + return; + } + } + } +} diff --git a/ProxyCore/Output/.svn/text-base/desktop.ini b/ProxyCore/Output/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Output/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Output/.svn/tmp/desktop.ini b/ProxyCore/Output/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Output/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Output/.svn/tmp/prop-base/desktop.ini b/ProxyCore/Output/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Output/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Output/.svn/tmp/props/desktop.ini b/ProxyCore/Output/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Output/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Output/.svn/tmp/text-base/desktop.ini b/ProxyCore/Output/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Output/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Output/TriggerData.cs b/ProxyCore/Output/TriggerData.cs new file mode 100644 index 0000000..8ca66b5 --- /dev/null +++ b/ProxyCore/Output/TriggerData.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using ProxyCore.Messages; + +namespace ProxyCore.Output +{ + public class TriggerData + { + internal TriggerData() + { + } + + /// + /// Match data. + /// + public Match Match + { + get; + internal set; + } + + /// + /// What we triggered on, you can change this value to replace the text. + /// + public Message Msg; + + /// + /// Custom argument if you registered a trigger to have one. + /// + public int Arg; + + /// + /// Replace matched %0 value with new string. This only works in regex triggers. You can have {%1} - {%n} in the new string for matched data. + /// Use {%%1} to escape. If you use % higher than what was captured then a NULL will be replaced there. + /// For example using %3 when there are only 2 things captured with regex. + /// + /// Example: + /// Line: "@mQuest Points @w: @Y19,361" + /// Pattern: "^@mQuest Points @w: (\s*)@Y([\d,]+)$" + /// Replace: "@mQuest Points @w: {%1}@G{%2}" + /// This would make quest points green in the "worth" command output. Where {%1} inserts the right amount + /// of spaces (what was captured) and {%2} inserts the quest points amount (19,361). + /// + /// New string to replace with. See function summary for help with this. + /// Allow parsing {%n} in the New string or not. If not then string + /// is replaced as is without parsing for arguments. + public void Replace(string New, bool AllowParse) + { + if(Match == null || !Match.Success) + return; + + if(AllowParse) + { + Match m; + if(New.Contains("{%")) // Make fast check first if we want to start messing with regex + { + while((m = _replaceRegex.Match(New)).Success) + { + int i; + if(!int.TryParse(m.Groups[1].Value, out i) || i >= Match.Groups.Count || i < 0) + New = New.Replace(m.Groups[0].Value, "NULL"); + else + New = New.Replace(m.Groups[0].Value, Match.Groups[i].Value); + } + } + if(New.Contains("{%%")) + { + while((m = _replaceRegex2.Match(New)).Success) + { + New = New.Replace(m.Groups[0].Value, "{%" + m.Groups[1].Value + "}"); + } + } + } + + // Don't use replace here because we only want to replace one occurance, wherever it was matched + Msg.Msg = Msg.Msg.Remove(Match.Index, Match.Length).Insert(Match.Index, New); + } + + private static readonly Regex _replaceRegex = new Regex(@"\{%(\d+)\}", RegexOptions.Compiled); + private static readonly Regex _replaceRegex2 = new Regex(@"\{%%(\d+)\}", RegexOptions.Compiled); + } +} diff --git a/ProxyCore/Output/TriggerEntry.cs b/ProxyCore/Output/TriggerEntry.cs new file mode 100644 index 0000000..76bfe6d --- /dev/null +++ b/ProxyCore/Output/TriggerEntry.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace ProxyCore.Output +{ + internal class TriggerEntry + { + internal Regex Pattern; + internal string PatternStr; + internal TriggerFunction Function; + internal int Priority; + internal string Name; + internal TriggerFlags Flags; + internal int Arg; + internal string Plugin; + internal List Disabled; + } + + /// + /// This is the function template that will be called when a trigger fires. Return true to gag it - this + /// will also prevent other triggers from triggering on this line. + /// + /// Triggered text data. + /// + public delegate bool TriggerFunction(TriggerData Data); + + [Flags] + public enum TriggerFlags + { + None = 0, + + /// + /// Normal triggers stop after finding the first match in a line, + /// with this flag the trigger repeats for each match in the same line. + /// This only applies if you use regex pattern. + /// + Repeat = 1, + + /// + /// Ignore lower and upper case. This only applies if you use regex pattern. + /// + CaseInsensitive = 2, + + /// + /// Pattern is not regex but instead just raw string. For example if you want + /// to trigger on "@w--> @WTICK @w<--" there's no need to create a regex pattern + /// just insert this string and set this flag in options and the trigger will be MUCH + /// faster. Use regex only where necessary. + /// + NotRegex = 4, + + /// + /// Trigger ignores color codes. If you set this flag you should NOT include any color codes + /// in your trigger pattern. + /// + NonAnsi = 8, + + /// + /// Matching will start from right and go to left. + /// + RightToLeft = 0x10, + } +} diff --git a/ProxyCore/Output/TriggerHandler.cs b/ProxyCore/Output/TriggerHandler.cs new file mode 100644 index 0000000..aa7af14 --- /dev/null +++ b/ProxyCore/Output/TriggerHandler.cs @@ -0,0 +1,345 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using ProxyCore.Messages; +using ProxyCore.Scripting; + +namespace ProxyCore.Output +{ + internal static class TriggerHandler + { + static TriggerHandler() + { + } + + private static SortedDictionary> Triggers = new SortedDictionary>(); + internal static Dictionary TriggersName = new Dictionary(); + + internal static void DisableTriggers(string FromPlugin, int MinPriority, int MaxPriority) + { + if(FromPlugin == null) + return; + FromPlugin = FromPlugin.ToLower().Trim(); + if(FromPlugin.Length == 0) + return; + foreach(KeyValuePair> x in Triggers) + { + if(x.Key < MinPriority) + continue; + if(x.Key > MaxPriority) + break; + + foreach(TriggerEntry y in x.Value) + { + if(y.Disabled == null) + y.Disabled = new List(); + if(!y.Disabled.Contains(FromPlugin)) + y.Disabled.Add(FromPlugin); + } + } + } + + internal static void EnableTriggers(string FromPlugin, int MinPriority, int MaxPriority) + { + if(FromPlugin == null) + return; + FromPlugin = FromPlugin.ToLower().Trim(); + if(FromPlugin.Length == 0) + return; + foreach(KeyValuePair> x in Triggers) + { + if(x.Key < MinPriority) + continue; + if(x.Key > MaxPriority) + break; + + foreach(TriggerEntry y in x.Value) + { + if(y.Disabled == null) + continue; + y.Disabled.Remove(FromPlugin); + } + } + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + internal static void RegisterTrigger(string Name, string Pattern, TriggerFunction Function) + { + RegisterTrigger(Name, Pattern, Function, TriggerFlags.None); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + internal static void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags) + { + RegisterTrigger(Name, Pattern, Function, Flags, 1000); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + /// Lower priority triggers get matched first. Default: 1000 + internal static void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags, int Priority) + { + RegisterTrigger(Name, Pattern, Function, Flags, Priority, 0, "core"); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + /// Lower priority triggers get matched first. Default: 1000 + /// Custom argument to pass to trigger data. + /// From which plugin was this registered. + internal static void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags, int Priority, int Arg, string Plugin) + { + if(string.IsNullOrEmpty(Pattern) || string.IsNullOrEmpty(Name) || Function == null) + return; + + Name = Name.ToLower().Trim(); + if(Name.Length == 0) + return; + + Regex p = null; + if((Flags & TriggerFlags.NotRegex) == TriggerFlags.None) + { + try + { + RegexOptions op = RegexOptions.None; + if((Flags & TriggerFlags.RightToLeft) != TriggerFlags.None) + op |= RegexOptions.RightToLeft; + if((Flags & TriggerFlags.CaseInsensitive) != TriggerFlags.None) + op |= RegexOptions.IgnoreCase; + p = new Regex(Pattern, op); + } + catch + { + return; + } + } + + TriggerEntry e = new TriggerEntry(); + e.Function = Function; + e.Pattern = p; + e.PatternStr = Pattern; + e.Priority = Priority; + e.Name = Name; + e.Flags = Flags; + e.Arg = Arg; + e.Plugin = Plugin; + + if(TriggersName.ContainsKey(Name)) + Triggers[TriggersName[Name].Priority].Remove(TriggersName[Name]); + + TriggersName[Name] = e; + if(!Triggers.ContainsKey(e.Priority)) + Triggers[e.Priority] = new List(); + Triggers[e.Priority].Add(e); + } + + /// + /// Unregister a trigger by name. + /// + /// Name of the trigger you wish to unregister. + internal static void UnregisterTrigger(string Name) + { + if(Name != null) + Name = Name.ToLower().Trim(); + if(string.IsNullOrEmpty(Name)) + return; + + if(!TriggersName.ContainsKey(Name)) + return; + + Triggers[TriggersName[Name].Priority].Remove(TriggersName[Name]); + TriggersName.Remove(Name); + } + + internal static void HandleText(string Msg, World world) + { + if(!string.IsNullOrEmpty(lastColorCode)) + Msg = lastColorCode + Msg; + lastColorCode = Colors.GetLastColorCode(Msg); + Msg = Colors.RemoveDuplicateColors(Msg); + + Message m = new Message(true); + m.Msg = Msg; + HandleLineRaw(m); + if(m.Msg != null) + { + world._SendMessage(m); + world.lastLine.Add(m.Msg); + while(world.lastLine.Count > 100) + world.lastLine.RemoveAt(0); + } + } + + private static string lastColorCode = ""; + + private static readonly List> gmcpData = + new List>(); + + internal static void HandleGMCP(string Msg) + { + //string origMsg = Msg; + string module; + try + { + int ind = Msg.IndexOf(' '); + if(ind == -1) + { + module = Msg; + Msg = ""; + } + else + { + module = Msg.Substring(0, ind); + Msg = Msg.Substring(ind).Trim(); + } + } + catch + { + return; + } + + if(string.IsNullOrEmpty(module)) + return; + + if(gmcpData.Count != 0) + gmcpData.Clear(); + module = module.ToLower(); + bool res = JSON.Parse(Msg, module, gmcpData); + if(!res) + return; + + if(gmcpData.Count == 0) + HandleGMCP(module, null); + else + { + foreach(KeyValuePair i in gmcpData) + HandleGMCP(i.Key.ToLower().Trim(), i.Value); + } + } + + private static void HandleGMCP(string Module, string Value) + { + Message m = new Message(true); + m.Msg = "$gmcp." + Module + (Value != null ? (" " + Value) : ""); + HandleLineRaw(m); + } + + private static void HandleLineRaw(Message Msg) + { + foreach(KeyValuePair x in PluginMgr.Plugins) + { + try + { + x.Value.OnReceivedLineBefore(Msg); + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } + if(Msg.Msg == null) + return; + } + + foreach(KeyValuePair> x in Triggers) + { + foreach(TriggerEntry y in x.Value) + { + if(y.Disabled != null && y.Disabled.Count != 0) + continue; + + int i = 0; + while(Msg.Msg != null) + { + int o = i; + Match m = null; + if((y.Flags & TriggerFlags.NotRegex) == TriggerFlags.None) + { + if((y.Flags & TriggerFlags.RightToLeft) != TriggerFlags.None) + m = y.Pattern.Match((y.Flags & TriggerFlags.NonAnsi) == TriggerFlags.None ? Msg.Msg : Msg.MsgNoColor); + else + m = y.Pattern.Match((y.Flags & TriggerFlags.NonAnsi) == TriggerFlags.None ? Msg.Msg : Msg.MsgNoColor, i); + if(!m.Success) + break; + } + else if(y.PatternStr != ((y.Flags & TriggerFlags.NonAnsi) == TriggerFlags.None ? Msg.Msg : Msg.MsgNoColor)) + break; + + TriggerData d = new TriggerData(); + d.Match = m; + d.Arg = y.Arg; + d.Msg = Msg; +#if !DEBUG + try + { +#endif + if(y.Function(d)) + { + Msg.Msg = null; + return; + } +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, y.Plugin); + } +#endif + + if((y.Flags & TriggerFlags.NotRegex) != TriggerFlags.None) + break; + if((y.Flags & TriggerFlags.RightToLeft) != TriggerFlags.None) + break; + + i = m.Groups[0].Index + m.Groups[0].Length; + if((y.Flags & TriggerFlags.Repeat) == TriggerFlags.None) + break; + + if(i == o) + i++; + } + } + } + + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.OnReceivedLineAfter(Msg); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + if(Msg.Msg == null) + return; + } + } + } +} diff --git a/ProxyCore/Output/desktop.ini b/ProxyCore/Output/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Output/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Properties/.svn/all-wcprops b/ProxyCore/Properties/.svn/all-wcprops new file mode 100644 index 0000000..ffb4463 --- /dev/null +++ b/ProxyCore/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/2/trunk/ProxyCore/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/!svn/ver/2/trunk/ProxyCore/Properties/AssemblyInfo.cs +END diff --git a/ProxyCore/Properties/.svn/desktop.ini b/ProxyCore/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Properties/.svn/dir-prop-base b/ProxyCore/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/ProxyCore/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/ProxyCore/Properties/.svn/entries b/ProxyCore/Properties/.svn/entries new file mode 100644 index 0000000..da997e1 --- /dev/null +++ b/ProxyCore/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/ProxyCore/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:43.228138Z +3655dd83493b526d4a7076bf3acfd0ed +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1448 + diff --git a/ProxyCore/Properties/.svn/prop-base/desktop.ini b/ProxyCore/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Properties/.svn/props/desktop.ini b/ProxyCore/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/ProxyCore/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..aef339e --- /dev/null +++ b/ProxyCore/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ProxyCore")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("ProxyCore")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("7d78ddc1-7213-4910-ba70-2a974d42ac5c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ProxyCore/Properties/.svn/text-base/desktop.ini b/ProxyCore/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Properties/.svn/tmp/desktop.ini b/ProxyCore/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Properties/.svn/tmp/prop-base/desktop.ini b/ProxyCore/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Properties/.svn/tmp/props/desktop.ini b/ProxyCore/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Properties/.svn/tmp/text-base/desktop.ini b/ProxyCore/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Properties/AssemblyInfo.cs b/ProxyCore/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..aef339e --- /dev/null +++ b/ProxyCore/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ProxyCore")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("ProxyCore")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("7d78ddc1-7213-4910-ba70-2a974d42ac5c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ProxyCore/Properties/desktop.ini b/ProxyCore/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/ProxyCore.csproj b/ProxyCore/ProxyCore.csproj new file mode 100644 index 0000000..801f5a4 --- /dev/null +++ b/ProxyCore/ProxyCore.csproj @@ -0,0 +1,112 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + Library + Properties + ProxyCore + ProxyCore + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\ + DEBUG;TRACE + prompt + 4 + bin\ProxyCore.XML + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\ + TRACE + prompt + 4 + + + + + + False + ..\Resources\Jayrock.dll + + + False + ..\Resources\Jayrock.Json.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/ProxyCore/Scripting/.svn/all-wcprops b/ProxyCore/Scripting/.svn/all-wcprops new file mode 100644 index 0000000..d93a56a --- /dev/null +++ b/ProxyCore/Scripting/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/40/trunk/ProxyCore/Scripting +END +Plugin.cs +K 25 +svn:wc:ra_dav:version-url +V 52 +/svn/!svn/ver/40/trunk/ProxyCore/Scripting/Plugin.cs +END +PluginMgr.cs +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/!svn/ver/2/trunk/ProxyCore/Scripting/PluginMgr.cs +END diff --git a/ProxyCore/Scripting/.svn/desktop.ini b/ProxyCore/Scripting/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Scripting/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Scripting/.svn/dir-prop-base b/ProxyCore/Scripting/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/ProxyCore/Scripting/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/ProxyCore/Scripting/.svn/entries b/ProxyCore/Scripting/.svn/entries new file mode 100644 index 0000000..cef0225 --- /dev/null +++ b/ProxyCore/Scripting/.svn/entries @@ -0,0 +1,96 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/ProxyCore/Scripting +http://proxymud.googlecode.com/svn + + + +2012-01-25T09:39:59.831649Z +40 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +PluginMgr.cs +file + + + + +2016-03-25T22:18:43.196138Z +ed07d3317b48945bad782841c613efd7 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +4277 + +Plugin.cs +file + + + + +2016-03-25T22:18:43.196138Z +967aa696b3d2aac48f62173975174294 +2012-01-25T09:39:59.831649Z +40 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +19884 + diff --git a/ProxyCore/Scripting/.svn/prop-base/desktop.ini b/ProxyCore/Scripting/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Scripting/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Scripting/.svn/props/desktop.ini b/ProxyCore/Scripting/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Scripting/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Scripting/.svn/text-base/Plugin.cs.svn-base b/ProxyCore/Scripting/.svn/text-base/Plugin.cs.svn-base new file mode 100644 index 0000000..4e4ac73 --- /dev/null +++ b/ProxyCore/Scripting/.svn/text-base/Plugin.cs.svn-base @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore.Output; +using System.Text.RegularExpressions; +using ProxyCore.Input; + +namespace ProxyCore.Scripting +{ + public class Plugin + { + protected Plugin(string keyword, string name) + { + Keyword = keyword; + Name = name; + } + + /// + /// Set this to be your configuration file if you want your plugin to have one. This is optional. + /// + public ConfigFile Config + { + get; + protected set; + } + + /// + /// Name of your plugin. You must set this or your plugin will not be loaded. + /// + public readonly string Name; + + /// + /// This is the keyword for your plugin. You must set this or your plugin will not be loaded. + /// This must be unique. If two or more plugins with the same keyword are found then the plugin + /// with the highest version number is loaded. + /// + public readonly string Keyword; + + /// + /// Creator of the plugin, this is your name / character's name. This is optional. + /// + public string Author + { + get; + protected set; + } + + /// + /// Version of your plugin. This is optional. + /// + public int Version + { + get; + protected set; + } + + /// + /// Description about your plugin. You should explain here what it does and how to handle it. + /// This will be displayed if user requests information about your plugin. This is optional. + /// + public string Description + { + get; + protected set; + } + + /// + /// Enter a website for this plugin if you wish. Mostly used to see documentation and updates. + /// + public string Website + { + get; + protected set; + } + + /// + /// Enter the URL for update checking txt file. For example "www.duckbat.com/plugins/update.moons.txt". + /// In the text file enter the number of last version. For example whole contents of the txt file can be "3". + /// Indicating that the last version for this plugin is 3. If version is greater than user's version and + /// they have update checking on then they will be notified that there is a more up to date version out there. + /// + public string UpdateUrl + { + get; + protected set; + } + + /// + /// Does this plugin require a certain version of core? Set this if there was an update and your plugin requires it. + /// Plugin is not loaded if an older version core than this is used and user will be notified. + /// + public int RequiredCoreVersion + { + get; + protected set; + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + protected void RegisterTrigger(string Name, string Pattern, TriggerFunction Function) + { + RegisterTrigger(Name, Pattern, Function, TriggerFlags.None); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + protected void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags) + { + RegisterTrigger(Name, Pattern, Function, Flags, 1000); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + /// Lower priority triggers get matched first. + protected void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags, int Priority) + { + RegisterTrigger(Name, Pattern, Function, Flags, Priority, 0); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + /// Lower priority triggers get matched first. + /// Custom argument that will be passed to trigger data. + protected void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags, int Priority, int Arg) + { + TriggerHandler.RegisterTrigger(Keyword.ToLower().Trim() + "." + Name, Pattern, Function, Flags, Priority, Arg, Keyword.ToLower().Trim()); + } + + /// + /// Unregister a trigger by name. + /// + /// Name of the trigger you wish to unregister. + protected void UnregisterTrigger(string Name) + { + TriggerHandler.UnregisterTrigger(Keyword.ToLower().Trim() + "." + Name); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f) + { + RegisterCommand(Cmd, Args, f, 0); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f, int MinLength) + { + RegisterCommand(Cmd, Args, f, MinLength, CMDFlags.None); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + /// Options for command. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f, int MinLength, CMDFlags flags) + { + RegisterCommand(Cmd, Args, f, MinLength, flags, null); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f, int MinLength, CMDFlags flags, string parent) + { + RegisterCommand(Cmd, Args, f, MinLength, flags, parent, 0); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + /// Custom argument to pass to function handler. This way you can register multiple commands to a same + /// function handler only differentiating them with this custom argument. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f, int MinLength, CMDFlags flags, string parent, int Arg) + { + RegisterCommand(Cmd, Args, f, MinLength, flags, parent, Arg, ulong.MaxValue); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + /// Custom argument to pass to function handler. This way you can register multiple commands to a same + /// function handler only differentiating them with this custom argument. + /// Mask of allowed auth levels to access this command. Default ulong.MaxValue (meaning all auth levels are allowed). + /// Enter 3 for example to allow only auth level 1 and 2 to access this command. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f, int MinLength, CMDFlags flags, string parent, int Arg, ulong AuthMask) + { + InputHandler.RegisterCommand(Cmd, Args, f, flags, parent, Arg, AuthMask, Keyword.ToLower().Trim(), MinLength); + } + + /// + /// Unregister a command. + /// + /// Command to unregister. If you want to unregister a nested command + /// separate commands with a space. + protected void UnregisterCommand(string Cmd) + { + InputHandler.UnregisterCommand(Cmd); + } + + /// + /// This will be called when character enters the game. Either by log in or reconnect. + /// + public virtual void OnLogin() + { + } + + /// + /// This will be called when we disconnect from Aardwolf. + /// + public virtual void OnDisconnect() + { + } + + /// + /// This will be called when we connect to Aardwolf. + /// + public virtual void OnConnect() + { + } + + /// + /// This is called when program shuts down. Write any code you need to shut down your plugin. + /// + public virtual void Shutdown() + { + } + + /// + /// This is called on every loop of world update. You can use it as your main loop for + /// the plugin if you need one. + /// + /// Current time since program startup. + public virtual void Update(long msTime) + { + } + + /// + /// This is called when we receive a line from MUD. It is called AFTER triggers are done with it. If + /// a trigger gagged the line this will not be called. + /// + /// + public virtual void OnReceivedLineAfter(Messages.Message Msg) + { + } + + /// + /// This is called when we receive a line from MUD. It is called BEFORE triggers are done with it. + /// + /// + public virtual void OnReceivedLineBefore(Messages.Message Msg) + { + } + + /// + /// This is called when user enters a command and inputhandler did not handle the command. So it + /// is called AFTER we check for aliases and commands and we are about to send command to MUD. + /// + /// Command that was entered. You can change this in the function. If you set null + /// then nothing will be sent to MUD. + /// Client who entered the command. If this is 0 it was executed from a plugin. + /// Auth level of who entered the command. + public virtual void OnEnteredCommandAfter(ref string Msg, uint ClientId, int AuthLevel) + { + } + + /// + /// This is called when user enters a command. It is called BEFORE we check for aliases and commands. + /// + /// Command that was entered. You can change this in the function. If you set null + /// then nothing will be sent to MUD and nothing will be checked for aliases or commands. + /// Client who entered the command. If this is 0 it was executed from a plugin. + /// Auth level of who entered the command. + public virtual void OnEnteredCommandBefore(ref string Msg, uint ClientId, int AuthLevel) + { + } + + /// + /// Enter required player config options here. This will be displayed if user requests info about a plugin. + /// For example you may enter here "echocommands ON" and "statmon ON" etc. Whatever your plugin requires. + /// This doesn't actually change the settings in game it is only for plugin info command. + /// + public readonly List RequiredPlayerConfig = new List(); + + /// + /// This is the class name of script. For example moons has this set to "MoonScript.MoonScript". + /// Only needed by developers who want to use another plugin in their plugin. + /// + internal string ClassName; + + /// + /// Called when we load a configuration file. + /// + /// Did the loading succeed? If not then the config file wasn't present and we created a new one. + public virtual void OnLoadedConfig(bool Success) + { + } + + /// + /// Disable all triggers with this priority. Disabling triggers from a plugin will make them not work until + /// you enable them from the same plugin again. If triggers have been disabled from multiple plugins then + /// all plugins will have to enable them again until they start working. Disabling triggers will make all + /// triggers with this priority to not work not only triggers in current plugin! + /// + /// Priority of triggers to disable. + protected void DisableTriggers(int Priority) + { + DisableTriggers(Priority, Priority); + } + + /// + /// Disable all triggers with this priority. Disabling triggers from a plugin will make them not work until + /// you enable them from the same plugin again. If triggers have been disabled from multiple plugins then + /// all plugins will have to enable them again until they start working. Disabling triggers will make all + /// triggers with this priority to not work not only triggers in current plugin! + /// + /// Minimum priority of triggers to disable. + /// Maximum priority of triggers to disable. + protected void DisableTriggers(int MinPriority, int MaxPriority) + { + TriggerHandler.DisableTriggers(Keyword, MinPriority, MaxPriority); + } + + /// + /// Enable all previously disabled triggers with this priority. + /// + /// Priority of triggers to enable. + protected void EnableTriggers(int Priority) + { + EnableTriggers(Priority, Priority); + } + + /// + /// Enable all previously disabled triggers with this priority. + /// + /// Minimum priority of triggers to enable. + /// Maximum priority of triggers to enable. + protected void EnableTriggers(int MinPriority, int MaxPriority) + { + TriggerHandler.EnableTriggers(Keyword, MinPriority, MaxPriority); + } + } +} diff --git a/ProxyCore/Scripting/.svn/text-base/PluginMgr.cs.svn-base b/ProxyCore/Scripting/.svn/text-base/PluginMgr.cs.svn-base new file mode 100644 index 0000000..75ba26a --- /dev/null +++ b/ProxyCore/Scripting/.svn/text-base/PluginMgr.cs.svn-base @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Reflection; + +namespace ProxyCore.Scripting +{ + public static class PluginMgr + { + internal static void LoadAll() + { + if(Plugins.Count != 0) + return; + + try + { + if(!Directory.Exists("plugins")) + { + Directory.CreateDirectory("plugins"); + return; + } + } + catch + { + return; + } + + string[] files = Directory.GetFiles("plugins", "*.dll"); + + if(files.Length != 0) + { + foreach(string file in files) + { + try + { + Assembly assembly = Assembly.LoadFrom(Path.GetFullPath(file)); + foreach(Type type in assembly.GetTypes()) + { + if(!type.IsClass || type.IsNotPublic) + continue; + + if(type.BaseType == typeof(Plugin)) + { + try + { + Plugin obj = (Plugin) Activator.CreateInstance(type); + obj.ClassName = type.ToString(); + if(obj.RequiredCoreVersion > World.Version) + throw new Exception("Newer version of core is needed! (" + obj.RequiredCoreVersion + ")"); + if(string.IsNullOrEmpty(obj.Keyword.Trim()) || string.IsNullOrEmpty(obj.Name.Trim())) + throw new Exception("Plugin has invalid parameters!"); + if(Plugins.ContainsKey(obj.Keyword.ToLower().Trim())) + { + Plugin prev = Plugins[obj.Keyword.ToLower().Trim()]; + if(prev.Version >= obj.Version) + throw new Exception("A newer version of this plugin was already loaded!"); + } + if(obj.Keyword.ToLower().Trim() == "core" || obj.Keyword.ToLower().Trim() == "server") + throw new Exception("Plugin has invalid keyword!"); + + if(obj.Config != null) + { + obj.Config.Load(obj.Keyword.ToLower().Trim()); + obj.OnLoadedConfig(obj.Config.DidLoad); + } + Plugins[obj.Keyword.ToLower().Trim()] = obj; + Log.Write("Loaded: [" + obj.Keyword.ToLower().Trim() + "] " + obj.Name + ", version " + obj.Version.ToString() + "."); + } + catch(Exception e) + { + Log.Write("Failed: [" + type.ToString() + "] in " + file + "!"); + Log.Write(" " + e.Message); + continue; + } + } + } + } + catch + { + continue; + } + } + } + + Log.Write("Done."); + } + + internal static Dictionary Plugins = new Dictionary(); + + /// + /// Get plugin by keyword. + /// + /// Keyword of plugin. + /// + public static Plugin GetPlugin(string Keyword) + { + Keyword = Keyword.ToLower().Trim(); + return Plugins.ContainsKey(Keyword) ? Plugins[Keyword] : null; + } + } +} diff --git a/ProxyCore/Scripting/.svn/text-base/desktop.ini b/ProxyCore/Scripting/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Scripting/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Scripting/.svn/tmp/desktop.ini b/ProxyCore/Scripting/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Scripting/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Scripting/.svn/tmp/prop-base/desktop.ini b/ProxyCore/Scripting/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Scripting/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Scripting/.svn/tmp/props/desktop.ini b/ProxyCore/Scripting/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Scripting/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Scripting/.svn/tmp/text-base/desktop.ini b/ProxyCore/Scripting/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Scripting/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Scripting/Plugin.cs b/ProxyCore/Scripting/Plugin.cs new file mode 100644 index 0000000..4e4ac73 --- /dev/null +++ b/ProxyCore/Scripting/Plugin.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore.Output; +using System.Text.RegularExpressions; +using ProxyCore.Input; + +namespace ProxyCore.Scripting +{ + public class Plugin + { + protected Plugin(string keyword, string name) + { + Keyword = keyword; + Name = name; + } + + /// + /// Set this to be your configuration file if you want your plugin to have one. This is optional. + /// + public ConfigFile Config + { + get; + protected set; + } + + /// + /// Name of your plugin. You must set this or your plugin will not be loaded. + /// + public readonly string Name; + + /// + /// This is the keyword for your plugin. You must set this or your plugin will not be loaded. + /// This must be unique. If two or more plugins with the same keyword are found then the plugin + /// with the highest version number is loaded. + /// + public readonly string Keyword; + + /// + /// Creator of the plugin, this is your name / character's name. This is optional. + /// + public string Author + { + get; + protected set; + } + + /// + /// Version of your plugin. This is optional. + /// + public int Version + { + get; + protected set; + } + + /// + /// Description about your plugin. You should explain here what it does and how to handle it. + /// This will be displayed if user requests information about your plugin. This is optional. + /// + public string Description + { + get; + protected set; + } + + /// + /// Enter a website for this plugin if you wish. Mostly used to see documentation and updates. + /// + public string Website + { + get; + protected set; + } + + /// + /// Enter the URL for update checking txt file. For example "www.duckbat.com/plugins/update.moons.txt". + /// In the text file enter the number of last version. For example whole contents of the txt file can be "3". + /// Indicating that the last version for this plugin is 3. If version is greater than user's version and + /// they have update checking on then they will be notified that there is a more up to date version out there. + /// + public string UpdateUrl + { + get; + protected set; + } + + /// + /// Does this plugin require a certain version of core? Set this if there was an update and your plugin requires it. + /// Plugin is not loaded if an older version core than this is used and user will be notified. + /// + public int RequiredCoreVersion + { + get; + protected set; + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + protected void RegisterTrigger(string Name, string Pattern, TriggerFunction Function) + { + RegisterTrigger(Name, Pattern, Function, TriggerFlags.None); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + protected void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags) + { + RegisterTrigger(Name, Pattern, Function, Flags, 1000); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + /// Lower priority triggers get matched first. + protected void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags, int Priority) + { + RegisterTrigger(Name, Pattern, Function, Flags, Priority, 0); + } + + /// + /// Register a new trigger. + /// + /// Unique identifier for the trigger. + /// Regex pattern for the trigger. + /// Function that will be called if this trigger fires. + /// Options for the trigger. + /// Lower priority triggers get matched first. + /// Custom argument that will be passed to trigger data. + protected void RegisterTrigger(string Name, string Pattern, TriggerFunction Function, TriggerFlags Flags, int Priority, int Arg) + { + TriggerHandler.RegisterTrigger(Keyword.ToLower().Trim() + "." + Name, Pattern, Function, Flags, Priority, Arg, Keyword.ToLower().Trim()); + } + + /// + /// Unregister a trigger by name. + /// + /// Name of the trigger you wish to unregister. + protected void UnregisterTrigger(string Name) + { + TriggerHandler.UnregisterTrigger(Keyword.ToLower().Trim() + "." + Name); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f) + { + RegisterCommand(Cmd, Args, f, 0); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f, int MinLength) + { + RegisterCommand(Cmd, Args, f, MinLength, CMDFlags.None); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + /// Options for command. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f, int MinLength, CMDFlags flags) + { + RegisterCommand(Cmd, Args, f, MinLength, flags, null); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f, int MinLength, CMDFlags flags, string parent) + { + RegisterCommand(Cmd, Args, f, MinLength, flags, parent, 0); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + /// Custom argument to pass to function handler. This way you can register multiple commands to a same + /// function handler only differentiating them with this custom argument. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f, int MinLength, CMDFlags flags, string parent, int Arg) + { + RegisterCommand(Cmd, Args, f, MinLength, flags, parent, Arg, ulong.MaxValue); + } + + /// + /// Register a new command or overwrite a previous one. + /// + /// Command to register. + /// Arguments to match (regex pattern). This can be set to null or empty string + /// if you don't want to capture anything or plan to do so in the function yourself. + /// Function of command. + /// Options for command. + /// Parent command (if you want to create a subcommand). You can enter commands separated with space if it's nested. + /// Custom argument to pass to function handler. This way you can register multiple commands to a same + /// function handler only differentiating them with this custom argument. + /// Mask of allowed auth levels to access this command. Default ulong.MaxValue (meaning all auth levels are allowed). + /// Enter 3 for example to allow only auth level 1 and 2 to access this command. + /// Minimum length of command typed required to activate. For example if command is "plugins" and this is 6 then "plugin" and "plugins" both activate this command but "plugi" won't. Enter 0 to disable this behaviour. + protected void RegisterCommand(string Cmd, string Args, CmdFunction f, int MinLength, CMDFlags flags, string parent, int Arg, ulong AuthMask) + { + InputHandler.RegisterCommand(Cmd, Args, f, flags, parent, Arg, AuthMask, Keyword.ToLower().Trim(), MinLength); + } + + /// + /// Unregister a command. + /// + /// Command to unregister. If you want to unregister a nested command + /// separate commands with a space. + protected void UnregisterCommand(string Cmd) + { + InputHandler.UnregisterCommand(Cmd); + } + + /// + /// This will be called when character enters the game. Either by log in or reconnect. + /// + public virtual void OnLogin() + { + } + + /// + /// This will be called when we disconnect from Aardwolf. + /// + public virtual void OnDisconnect() + { + } + + /// + /// This will be called when we connect to Aardwolf. + /// + public virtual void OnConnect() + { + } + + /// + /// This is called when program shuts down. Write any code you need to shut down your plugin. + /// + public virtual void Shutdown() + { + } + + /// + /// This is called on every loop of world update. You can use it as your main loop for + /// the plugin if you need one. + /// + /// Current time since program startup. + public virtual void Update(long msTime) + { + } + + /// + /// This is called when we receive a line from MUD. It is called AFTER triggers are done with it. If + /// a trigger gagged the line this will not be called. + /// + /// + public virtual void OnReceivedLineAfter(Messages.Message Msg) + { + } + + /// + /// This is called when we receive a line from MUD. It is called BEFORE triggers are done with it. + /// + /// + public virtual void OnReceivedLineBefore(Messages.Message Msg) + { + } + + /// + /// This is called when user enters a command and inputhandler did not handle the command. So it + /// is called AFTER we check for aliases and commands and we are about to send command to MUD. + /// + /// Command that was entered. You can change this in the function. If you set null + /// then nothing will be sent to MUD. + /// Client who entered the command. If this is 0 it was executed from a plugin. + /// Auth level of who entered the command. + public virtual void OnEnteredCommandAfter(ref string Msg, uint ClientId, int AuthLevel) + { + } + + /// + /// This is called when user enters a command. It is called BEFORE we check for aliases and commands. + /// + /// Command that was entered. You can change this in the function. If you set null + /// then nothing will be sent to MUD and nothing will be checked for aliases or commands. + /// Client who entered the command. If this is 0 it was executed from a plugin. + /// Auth level of who entered the command. + public virtual void OnEnteredCommandBefore(ref string Msg, uint ClientId, int AuthLevel) + { + } + + /// + /// Enter required player config options here. This will be displayed if user requests info about a plugin. + /// For example you may enter here "echocommands ON" and "statmon ON" etc. Whatever your plugin requires. + /// This doesn't actually change the settings in game it is only for plugin info command. + /// + public readonly List RequiredPlayerConfig = new List(); + + /// + /// This is the class name of script. For example moons has this set to "MoonScript.MoonScript". + /// Only needed by developers who want to use another plugin in their plugin. + /// + internal string ClassName; + + /// + /// Called when we load a configuration file. + /// + /// Did the loading succeed? If not then the config file wasn't present and we created a new one. + public virtual void OnLoadedConfig(bool Success) + { + } + + /// + /// Disable all triggers with this priority. Disabling triggers from a plugin will make them not work until + /// you enable them from the same plugin again. If triggers have been disabled from multiple plugins then + /// all plugins will have to enable them again until they start working. Disabling triggers will make all + /// triggers with this priority to not work not only triggers in current plugin! + /// + /// Priority of triggers to disable. + protected void DisableTriggers(int Priority) + { + DisableTriggers(Priority, Priority); + } + + /// + /// Disable all triggers with this priority. Disabling triggers from a plugin will make them not work until + /// you enable them from the same plugin again. If triggers have been disabled from multiple plugins then + /// all plugins will have to enable them again until they start working. Disabling triggers will make all + /// triggers with this priority to not work not only triggers in current plugin! + /// + /// Minimum priority of triggers to disable. + /// Maximum priority of triggers to disable. + protected void DisableTriggers(int MinPriority, int MaxPriority) + { + TriggerHandler.DisableTriggers(Keyword, MinPriority, MaxPriority); + } + + /// + /// Enable all previously disabled triggers with this priority. + /// + /// Priority of triggers to enable. + protected void EnableTriggers(int Priority) + { + EnableTriggers(Priority, Priority); + } + + /// + /// Enable all previously disabled triggers with this priority. + /// + /// Minimum priority of triggers to enable. + /// Maximum priority of triggers to enable. + protected void EnableTriggers(int MinPriority, int MaxPriority) + { + TriggerHandler.EnableTriggers(Keyword, MinPriority, MaxPriority); + } + } +} diff --git a/ProxyCore/Scripting/PluginMgr.cs b/ProxyCore/Scripting/PluginMgr.cs new file mode 100644 index 0000000..75ba26a --- /dev/null +++ b/ProxyCore/Scripting/PluginMgr.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Reflection; + +namespace ProxyCore.Scripting +{ + public static class PluginMgr + { + internal static void LoadAll() + { + if(Plugins.Count != 0) + return; + + try + { + if(!Directory.Exists("plugins")) + { + Directory.CreateDirectory("plugins"); + return; + } + } + catch + { + return; + } + + string[] files = Directory.GetFiles("plugins", "*.dll"); + + if(files.Length != 0) + { + foreach(string file in files) + { + try + { + Assembly assembly = Assembly.LoadFrom(Path.GetFullPath(file)); + foreach(Type type in assembly.GetTypes()) + { + if(!type.IsClass || type.IsNotPublic) + continue; + + if(type.BaseType == typeof(Plugin)) + { + try + { + Plugin obj = (Plugin) Activator.CreateInstance(type); + obj.ClassName = type.ToString(); + if(obj.RequiredCoreVersion > World.Version) + throw new Exception("Newer version of core is needed! (" + obj.RequiredCoreVersion + ")"); + if(string.IsNullOrEmpty(obj.Keyword.Trim()) || string.IsNullOrEmpty(obj.Name.Trim())) + throw new Exception("Plugin has invalid parameters!"); + if(Plugins.ContainsKey(obj.Keyword.ToLower().Trim())) + { + Plugin prev = Plugins[obj.Keyword.ToLower().Trim()]; + if(prev.Version >= obj.Version) + throw new Exception("A newer version of this plugin was already loaded!"); + } + if(obj.Keyword.ToLower().Trim() == "core" || obj.Keyword.ToLower().Trim() == "server") + throw new Exception("Plugin has invalid keyword!"); + + if(obj.Config != null) + { + obj.Config.Load(obj.Keyword.ToLower().Trim()); + obj.OnLoadedConfig(obj.Config.DidLoad); + } + Plugins[obj.Keyword.ToLower().Trim()] = obj; + Log.Write("Loaded: [" + obj.Keyword.ToLower().Trim() + "] " + obj.Name + ", version " + obj.Version.ToString() + "."); + } + catch(Exception e) + { + Log.Write("Failed: [" + type.ToString() + "] in " + file + "!"); + Log.Write(" " + e.Message); + continue; + } + } + } + } + catch + { + continue; + } + } + } + + Log.Write("Done."); + } + + internal static Dictionary Plugins = new Dictionary(); + + /// + /// Get plugin by keyword. + /// + /// Keyword of plugin. + /// + public static Plugin GetPlugin(string Keyword) + { + Keyword = Keyword.ToLower().Trim(); + return Plugins.ContainsKey(Keyword) ? Plugins[Keyword] : null; + } + } +} diff --git a/ProxyCore/Scripting/desktop.ini b/ProxyCore/Scripting/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Scripting/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Utility/.svn/all-wcprops b/ProxyCore/Utility/.svn/all-wcprops new file mode 100644 index 0000000..07d8c46 --- /dev/null +++ b/ProxyCore/Utility/.svn/all-wcprops @@ -0,0 +1,35 @@ +K 25 +svn:wc:ra_dav:version-url +V 40 +/svn/!svn/ver/54/trunk/ProxyCore/Utility +END +Config.cs +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/!svn/ver/2/trunk/ProxyCore/Utility/Config.cs +END +JSON.cs +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2/trunk/ProxyCore/Utility/JSON.cs +END +Wrap.cs +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2/trunk/ProxyCore/Utility/Wrap.cs +END +ServerConfig.cs +K 25 +svn:wc:ra_dav:version-url +V 56 +/svn/!svn/ver/54/trunk/ProxyCore/Utility/ServerConfig.cs +END +Colors.cs +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/!svn/ver/4/trunk/ProxyCore/Utility/Colors.cs +END diff --git a/ProxyCore/Utility/.svn/desktop.ini b/ProxyCore/Utility/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Utility/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Utility/.svn/dir-prop-base b/ProxyCore/Utility/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/ProxyCore/Utility/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/ProxyCore/Utility/.svn/entries b/ProxyCore/Utility/.svn/entries new file mode 100644 index 0000000..39f784d --- /dev/null +++ b/ProxyCore/Utility/.svn/entries @@ -0,0 +1,198 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/ProxyCore/Utility +http://proxymud.googlecode.com/svn + + + +2012-02-02T08:38:33.263906Z +54 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +Colors.cs +file + + + + +2016-03-25T22:18:43.212138Z +5ae2e0fde8eb3334d8301d5df234e3d2 +2012-01-17T12:29:36.909344Z +4 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +30665 + +Config.cs +file + + + + +2016-03-25T22:18:43.212138Z +5a3c22435b419310b907c98d9d5211dd +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +11921 + +JSON.cs +file + + + + +2016-03-25T22:18:43.212138Z +4a1b0482f41a0b953be7acb4bb9f378b +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1750 + +Wrap.cs +file + + + + +2016-03-25T22:18:43.212138Z +2cbea5ce4ee9b94dc77d7b0ad48df18d +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +4490 + +ServerConfig.cs +file + + + + +2016-03-25T22:18:43.212138Z +3b3013b1dd6d65734b147b7761f01d59 +2012-02-02T08:38:33.263906Z +54 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +2782 + diff --git a/ProxyCore/Utility/.svn/prop-base/desktop.ini b/ProxyCore/Utility/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Utility/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Utility/.svn/props/desktop.ini b/ProxyCore/Utility/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Utility/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Utility/.svn/text-base/Colors.cs.svn-base b/ProxyCore/Utility/.svn/text-base/Colors.cs.svn-base new file mode 100644 index 0000000..1498ab1 --- /dev/null +++ b/ProxyCore/Utility/.svn/text-base/Colors.cs.svn-base @@ -0,0 +1,748 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ProxyCore +{ + public static class Colors + { + static Colors() + { + XTermToAnsiTable[0] = 'w'; + XTermToAnsiTable[1] = 'r'; + XTermToAnsiTable[2] = 'g'; + XTermToAnsiTable[3] = 'y'; + XTermToAnsiTable[4] = 'b'; + XTermToAnsiTable[5] = 'm'; + XTermToAnsiTable[6] = 'c'; + XTermToAnsiTable[7] = 'w'; + XTermToAnsiTable[8] = 'D'; + XTermToAnsiTable[9] = 'R'; + XTermToAnsiTable[10] = 'G'; + XTermToAnsiTable[11] = 'Y'; + XTermToAnsiTable[12] = 'B'; + XTermToAnsiTable[13] = 'M'; + XTermToAnsiTable[14] = 'C'; + XTermToAnsiTable[15] = 'W'; + XTermToAnsiTable[16] = 'w'; + XTermToAnsiTable[17] = 'b'; + XTermToAnsiTable[18] = 'b'; + XTermToAnsiTable[19] = 'b'; + XTermToAnsiTable[20] = 'B'; + XTermToAnsiTable[21] = 'B'; + XTermToAnsiTable[22] = 'g'; + XTermToAnsiTable[23] = 'c'; + XTermToAnsiTable[24] = 'c'; + XTermToAnsiTable[25] = 'c'; + XTermToAnsiTable[26] = 'c'; + XTermToAnsiTable[27] = 'B'; + XTermToAnsiTable[28] = 'g'; + XTermToAnsiTable[29] = 'c'; + XTermToAnsiTable[30] = 'c'; + XTermToAnsiTable[31] = 'c'; + XTermToAnsiTable[32] = 'c'; + XTermToAnsiTable[33] = 'C'; + XTermToAnsiTable[34] = 'g'; + XTermToAnsiTable[35] = 'c'; + XTermToAnsiTable[36] = 'c'; + XTermToAnsiTable[37] = 'c'; + XTermToAnsiTable[38] = 'C'; + XTermToAnsiTable[39] = 'C'; + XTermToAnsiTable[40] = 'G'; + XTermToAnsiTable[41] = 'c'; + XTermToAnsiTable[42] = 'c'; + XTermToAnsiTable[43] = 'C'; + XTermToAnsiTable[44] = 'C'; + XTermToAnsiTable[45] = 'C'; + XTermToAnsiTable[46] = 'G'; + XTermToAnsiTable[47] = 'G'; + XTermToAnsiTable[48] = 'C'; + XTermToAnsiTable[49] = 'C'; + XTermToAnsiTable[50] = 'C'; + XTermToAnsiTable[51] = 'C'; + XTermToAnsiTable[52] = 'r'; + XTermToAnsiTable[53] = 'm'; + XTermToAnsiTable[54] = 'm'; + XTermToAnsiTable[55] = 'm'; + XTermToAnsiTable[56] = 'm'; + XTermToAnsiTable[57] = 'B'; + XTermToAnsiTable[58] = 'y'; + XTermToAnsiTable[59] = 'D'; + XTermToAnsiTable[60] = 'D'; + XTermToAnsiTable[61] = 'D'; + XTermToAnsiTable[62] = 'D'; + XTermToAnsiTable[63] = 'B'; + XTermToAnsiTable[64] = 'y'; + XTermToAnsiTable[65] = 'D'; + XTermToAnsiTable[66] = 'D'; + XTermToAnsiTable[67] = 'D'; + XTermToAnsiTable[68] = 'D'; + XTermToAnsiTable[69] = 'w'; + XTermToAnsiTable[70] = 'y'; + XTermToAnsiTable[71] = 'D'; + XTermToAnsiTable[72] = 'D'; + XTermToAnsiTable[73] = 'D'; + XTermToAnsiTable[74] = 'w'; + XTermToAnsiTable[75] = 'w'; + XTermToAnsiTable[76] = 'y'; + XTermToAnsiTable[77] = 'D'; + XTermToAnsiTable[78] = 'D'; + XTermToAnsiTable[79] = 'w'; + XTermToAnsiTable[80] = 'w'; + XTermToAnsiTable[81] = 'C'; + XTermToAnsiTable[82] = 'G'; + XTermToAnsiTable[83] = 'G'; + XTermToAnsiTable[84] = 'w'; + XTermToAnsiTable[85] = 'w'; + XTermToAnsiTable[86] = 'C'; + XTermToAnsiTable[87] = 'C'; + XTermToAnsiTable[88] = 'r'; + XTermToAnsiTable[89] = 'm'; + XTermToAnsiTable[90] = 'm'; + XTermToAnsiTable[91] = 'm'; + XTermToAnsiTable[92] = 'm'; + XTermToAnsiTable[93] = 'M'; + XTermToAnsiTable[94] = 'y'; + XTermToAnsiTable[95] = 'D'; + XTermToAnsiTable[96] = 'D'; + XTermToAnsiTable[97] = 'D'; + XTermToAnsiTable[98] = 'D'; + XTermToAnsiTable[99] = 'w'; + XTermToAnsiTable[100] = 'y'; + XTermToAnsiTable[101] = 'D'; + XTermToAnsiTable[102] = 'D'; + XTermToAnsiTable[103] = 'D'; + XTermToAnsiTable[104] = 'w'; + XTermToAnsiTable[105] = 'w'; + XTermToAnsiTable[106] = 'y'; + XTermToAnsiTable[107] = 'D'; + XTermToAnsiTable[108] = 'D'; + XTermToAnsiTable[109] = 'w'; + XTermToAnsiTable[110] = 'w'; + XTermToAnsiTable[111] = 'w'; + XTermToAnsiTable[112] = 'y'; + XTermToAnsiTable[113] = 'D'; + XTermToAnsiTable[114] = 'w'; + XTermToAnsiTable[115] = 'w'; + XTermToAnsiTable[116] = 'w'; + XTermToAnsiTable[117] = 'w'; + XTermToAnsiTable[118] = 'Y'; + XTermToAnsiTable[119] = 'w'; + XTermToAnsiTable[120] = 'w'; + XTermToAnsiTable[121] = 'w'; + XTermToAnsiTable[122] = 'w'; + XTermToAnsiTable[123] = 'w'; + XTermToAnsiTable[124] = 'r'; + XTermToAnsiTable[125] = 'm'; + XTermToAnsiTable[126] = 'm'; + XTermToAnsiTable[127] = 'm'; + XTermToAnsiTable[128] = 'M'; + XTermToAnsiTable[129] = 'M'; + XTermToAnsiTable[130] = 'y'; + XTermToAnsiTable[131] = 'D'; + XTermToAnsiTable[132] = 'D'; + XTermToAnsiTable[133] = 'D'; + XTermToAnsiTable[134] = 'w'; + XTermToAnsiTable[135] = 'w'; + XTermToAnsiTable[136] = 'y'; + XTermToAnsiTable[137] = 'D'; + XTermToAnsiTable[138] = 'D'; + XTermToAnsiTable[139] = 'w'; + XTermToAnsiTable[140] = 'w'; + XTermToAnsiTable[141] = 'w'; + XTermToAnsiTable[142] = 'y'; + XTermToAnsiTable[143] = 'D'; + XTermToAnsiTable[144] = 'w'; + XTermToAnsiTable[145] = 'w'; + XTermToAnsiTable[146] = 'w'; + XTermToAnsiTable[147] = 'w'; + XTermToAnsiTable[148] = 'Y'; + XTermToAnsiTable[149] = 'w'; + XTermToAnsiTable[150] = 'w'; + XTermToAnsiTable[151] = 'w'; + XTermToAnsiTable[152] = 'w'; + XTermToAnsiTable[153] = 'w'; + XTermToAnsiTable[154] = 'Y'; + XTermToAnsiTable[155] = 'w'; + XTermToAnsiTable[156] = 'w'; + XTermToAnsiTable[157] = 'w'; + XTermToAnsiTable[158] = 'w'; + XTermToAnsiTable[159] = 'W'; + XTermToAnsiTable[160] = 'R'; + XTermToAnsiTable[161] = 'm'; + XTermToAnsiTable[162] = 'm'; + XTermToAnsiTable[163] = 'M'; + XTermToAnsiTable[164] = 'M'; + XTermToAnsiTable[165] = 'M'; + XTermToAnsiTable[166] = 'y'; + XTermToAnsiTable[167] = 'D'; + XTermToAnsiTable[168] = 'D'; + XTermToAnsiTable[169] = 'w'; + XTermToAnsiTable[170] = 'w'; + XTermToAnsiTable[171] = 'M'; + XTermToAnsiTable[172] = 'y'; + XTermToAnsiTable[173] = 'D'; + XTermToAnsiTable[174] = 'w'; + XTermToAnsiTable[175] = 'w'; + XTermToAnsiTable[176] = 'w'; + XTermToAnsiTable[177] = 'w'; + XTermToAnsiTable[178] = 'Y'; + XTermToAnsiTable[179] = 'w'; + XTermToAnsiTable[180] = 'w'; + XTermToAnsiTable[181] = 'w'; + XTermToAnsiTable[182] = 'w'; + XTermToAnsiTable[183] = 'w'; + XTermToAnsiTable[184] = 'Y'; + XTermToAnsiTable[185] = 'w'; + XTermToAnsiTable[186] = 'w'; + XTermToAnsiTable[187] = 'w'; + XTermToAnsiTable[188] = 'w'; + XTermToAnsiTable[189] = 'W'; + XTermToAnsiTable[190] = 'Y'; + XTermToAnsiTable[191] = 'Y'; + XTermToAnsiTable[192] = 'w'; + XTermToAnsiTable[193] = 'w'; + XTermToAnsiTable[194] = 'W'; + XTermToAnsiTable[195] = 'W'; + XTermToAnsiTable[196] = 'R'; + XTermToAnsiTable[197] = 'R'; + XTermToAnsiTable[198] = 'M'; + XTermToAnsiTable[199] = 'M'; + XTermToAnsiTable[200] = 'M'; + XTermToAnsiTable[201] = 'M'; + XTermToAnsiTable[202] = 'R'; + XTermToAnsiTable[203] = 'R'; + XTermToAnsiTable[204] = 'w'; + XTermToAnsiTable[205] = 'w'; + XTermToAnsiTable[206] = 'M'; + XTermToAnsiTable[207] = 'M'; + XTermToAnsiTable[208] = 'Y'; + XTermToAnsiTable[209] = 'w'; + XTermToAnsiTable[210] = 'w'; + XTermToAnsiTable[211] = 'w'; + XTermToAnsiTable[212] = 'w'; + XTermToAnsiTable[213] = 'w'; + XTermToAnsiTable[214] = 'Y'; + XTermToAnsiTable[215] = 'w'; + XTermToAnsiTable[216] = 'w'; + XTermToAnsiTable[217] = 'w'; + XTermToAnsiTable[218] = 'w'; + XTermToAnsiTable[219] = 'W'; + XTermToAnsiTable[220] = 'Y'; + XTermToAnsiTable[221] = 'Y'; + XTermToAnsiTable[222] = 'w'; + XTermToAnsiTable[223] = 'w'; + XTermToAnsiTable[224] = 'W'; + XTermToAnsiTable[225] = 'W'; + XTermToAnsiTable[226] = 'Y'; + XTermToAnsiTable[227] = 'Y'; + XTermToAnsiTable[228] = 'w'; + XTermToAnsiTable[229] = 'W'; + XTermToAnsiTable[230] = 'W'; + XTermToAnsiTable[231] = 'W'; + XTermToAnsiTable[232] = 'w'; + XTermToAnsiTable[233] = 'w'; + XTermToAnsiTable[234] = 'w'; + XTermToAnsiTable[235] = 'w'; + XTermToAnsiTable[236] = 'w'; + XTermToAnsiTable[237] = 'w'; + XTermToAnsiTable[238] = 'D'; + XTermToAnsiTable[239] = 'D'; + XTermToAnsiTable[240] = 'D'; + XTermToAnsiTable[241] = 'D'; + XTermToAnsiTable[242] = 'D'; + XTermToAnsiTable[243] = 'D'; + XTermToAnsiTable[244] = 'D'; + XTermToAnsiTable[245] = 'D'; + XTermToAnsiTable[246] = 'D'; + XTermToAnsiTable[247] = 'D'; + XTermToAnsiTable[248] = 'w'; + XTermToAnsiTable[249] = 'w'; + XTermToAnsiTable[250] = 'w'; + XTermToAnsiTable[251] = 'w'; + XTermToAnsiTable[252] = 'w'; + XTermToAnsiTable[253] = 'w'; + XTermToAnsiTable[254] = 'W'; + XTermToAnsiTable[255] = 'W'; + } + + /// + /// Check if a char code is ANSI color code. + /// + /// Char to check for. + /// + public static bool IsColorCode(char code) + { + // Don't allow black color, we can't see it + if(code == 'd') + return false; + + foreach(ColorEntry x in ColorEntries) + { + if(x.ColorChar == code) + return true; + } + + return false; + } + + /// + /// Convert normal colors to HTML colors. It converts inserting </font> in front of every color too + /// so you need to have started with some font already. + /// + /// Message to convert colors in. + /// + public static string GetHTMLColors(string Msg) + { + Msg = Msg.Replace("@@", colorEscape); + foreach(ColorEntry c in ColorEntries) + Msg = Msg.Replace("@" + c.ColorChar, ""); + Msg = Msg.Replace(colorEscape, "@"); + return Msg; + } + + // Any codes that aren't xterm and aren't listed here will be @e instead of escape char. + private static ColorEntry[] ColorEntries = new[] + { + new ColorEntry('w', "", "", "grey", "C0C0C0"), + new ColorEntry('W', "", "", "white", "FFFFFF"), + new ColorEntry('G', "", "", "green", "00FF00"), + new ColorEntry('g', "", "", "dark green", "008000"), + new ColorEntry('R', "", "", "red", "FF0000"), + new ColorEntry('r', "", "", "dark red", "800000"), + new ColorEntry('Y', "", "", "yellow", "FFFF00"), + new ColorEntry('y', "", "", "dark yellow", "808000"), + new ColorEntry('C', "", "", "cyan", "00FFFF"), + new ColorEntry('c', "", "", "dark cyan", "008080"), + new ColorEntry('B', "", "", "blue", "0000FF"), + new ColorEntry('b', "", "", "dark blue", "000080"), + new ColorEntry('M', "", "", "magenta", "FF00FF"), + new ColorEntry('m', "", "", "dark magenta", "800080"), + new ColorEntry('D', "", "", "dark grey", "808080"), + new ColorEntry('d', "", "", "black", "000000"), + new ColorEntry('q', "", "", "default", "C0C0C0") + }; + + /// + /// Get last color code in the string (not including foreground colors). This will return + /// with the @ sign. + /// + /// + /// + public static string GetLastColorCode(string text) + { + text = text.Replace("@@", colorEscape); + + // Start checking from the end + for(int i = text.Length - 1; i >= 0; i--) + { + if(text[i] == '@' && i + 1 < text.Length && text[i + 1] != 'f' && text[i + 1] != 'y') + { + if(text[i + 1] == 'x') + { + int len; + string code = GetXTerm(text, i, out len).ToString(); + if(len == 0) + continue; + + if(code.Length == 1) + code = "00" + code; + else if(code.Length == 2) + code = "0" + code; + + return "@x" + code; + } + else + return "@" + text[i + 1]; + } + } + + return string.Empty; + } + + /// + /// Removes duplicate colors from a string. For example "@r @Y bla@w" would become " @Ybla". + /// Also XTERM color codes become padded with zero if they are present. + /// + /// String to fix colors in. + /// + public static string RemoveDuplicateColors(string orig) + { + StringBuilder txt = new StringBuilder(); + + string currentColor = ""; + bool didWriteColor = false; + bool hasAt = false; + bool hasE = false; + + for(int i = 0; i < orig.Length; i++) + { + switch(orig[i]) + { + case '@': + { + if(hasAt) + { + if(!didWriteColor) + { + txt.Append(currentColor); + didWriteColor = true; + } + + txt.Append("@@"); + hasAt = false; + } + else + { + hasAt = true; + } + } break; + + case ' ': + case '\r': + case '\n': + case '\t': + txt.Append(orig[i]); + break; + + default: + { + if(hasAt) + { + if(orig[i] == 'e') + { + txt.Append("@e"); + hasAt = false; + hasE = true; + continue; + } + if(orig[i] == 'f') + { + if(i + 1 < orig.Length) + txt.Append("@f" + orig[i + 1]); + i++; + hasAt = false; + continue; + } + if(orig[i] == 'z') + { + int len; + string code = GetXTerm(orig, i - 1, out len).ToString(); + if(len > 0) + { + if(code.Length == 1) + code = "00" + code; + else if(code.Length == 2) + code = "0" + code; + txt.Append("@z" + code); + i += len; + } + hasAt = false; + continue; + } + if(orig[i] == 'x') + { + int len; + string code = GetXTerm(orig, i - 1, out len).ToString(); + if(len > 0) + { + if(code.Length == 1) + code = "00" + code; + else if(code.Length == 2) + code = "0" + code; + i += len; + if(currentColor != "@x" + code) + { + currentColor = "@x" + code; + didWriteColor = false; + } + } + hasAt = false; + continue; + } + if(currentColor != "@" + orig[i]) + { + currentColor = "@" + orig[i]; + didWriteColor = false; + } + hasAt = false; + } + else if(hasE) + { + txt.Append(orig[i]); + if(orig[i] == 'm') + hasE = false; + } + else + { + if(!didWriteColor) + { + txt.Append(currentColor); + didWriteColor = true; + } + + txt.Append(orig[i]); + } + } break; + } + } + + return txt.ToString(); + } + + /// + /// This is what we turn the @ into so we can replace colors and keep the symbol intact + /// + private const string colorEscape = "#\r\ncolor_escape_sequence\r\n#"; + + /// + /// This function will turn either ANSI color to our format or vice versa. + /// + /// String passed for color changing. + /// False means we change our format (@x) to ANSI; true means we change ANSI to our format (we don't convert XTERM colors this way though). + /// Allow replacing xterm colors? If disabled we will replace into closest ANSI. + /// + public static string FixColors(string text, bool fromRaw, bool allowXTerm) + { + if(fromRaw == false) + { + text = text.Replace("@@", colorEscape); + text = ReplaceXTerm(text, fromRaw, allowXTerm); + for(int i = 0; i < ColorEntries.Length; i++) + { + text = text.Replace("@" + ColorEntries[i].ColorChar, ColorEntries[i].ANSI); + text = text.Replace("@f" + ColorEntries[i].ColorChar, ColorEntries[i].ForeANSI); + } + text = text.Replace("@e", ""); + text = text.Replace(colorEscape, "@"); + } + else + { + text = text.Replace("@", colorEscape); + text = ReplaceXTerm(text, fromRaw, allowXTerm); + for(int i = 0; i < ColorEntries.Length; i++) + { + text = text.Replace(ColorEntries[i].ANSI, "@" + ColorEntries[i].ColorChar); + text = text.Replace(ColorEntries[i].ForeANSI, "@f" + ColorEntries[i].ColorChar); + } + text = text.Replace("", "@e"); + text = text.Replace(colorEscape, "@@"); + } + return text; + } + + /// + /// Remove dark grey color code from string and replace it with normal grey. + /// + /// String to remove dark grey from (replacing it with normal grey). + /// + public static string RemoveGray(string text) + { + text = text.Replace("@@", colorEscape); + text = text.Replace("@D", "@w"); + text = text.Replace("@fD", "@fw"); + text = text.Replace(colorEscape, "@@"); + return text; + } + + /// + /// This function will Remove all color from the string. + /// + /// String where the color is being removed. + /// True means raw color (ansi); false means our format (@x). + /// + public static string RemoveColors(string text, bool raw) + { + if(!raw) + { + text = text.Replace("@@", colorEscape); + for(int i = 0; i < ColorEntries.Length; i++) + { + text = text.Replace("@" + ColorEntries[i].ColorChar, ""); + text = text.Replace("@f" + ColorEntries[i].ColorChar, ""); + } + int j; + while((j = text.IndexOf("@x")) != -1 || (j = text.IndexOf("@z")) != -1) + text = text.Remove(j, 5); + text = text.Replace(colorEscape, "@"); + return text; + } + + for(int i = 0; i < ColorEntries.Length; i++) + { + text = text.Replace(ColorEntries[i].ANSI, ""); + text = text.Replace(ColorEntries[i].ForeANSI, ""); + } + + int k; + while((k = text.IndexOf("")) != -1) + { + int l = text.IndexOf('m', k); + if(l == -1) + break; + text = text.Remove(k, l - k); + } + return text; + } + + /// + /// Get XTerm color code from text in index (index must be at @x or @z, before it). + /// + /// Text to get color code from. + /// Index of @z or @x. + /// + public static byte GetXTerm(string text, int index, out int xlen) + { + // Skip @x or @z + index += 2; + + // Get byte number now + byte v = GetXTermCode(text, index, out xlen); + return v; + } + + /// + /// Get full escape string from string in index. + /// + /// Text to search from. + /// Index in text of @e + /// Length of the thing behind @e. + /// + public static string GetEscape(string text, int index, out int xlen) + { + index += 2; + if((xlen = text.IndexOf('m', index)) == -1) + { + xlen = 0; + return "@e[0m"; + } + + xlen++; // include the 'm' + xlen -= index; + return "@e" + text.Substring(index, xlen); + } + + /// + /// Get XTerm color code from text in index (index must be at the number not before @x or @z like the other function) + /// + /// Text to get color code from. + /// Index of number. + /// Length of the number. + /// + public static byte GetXTermCode(string text, int index, out int xlen) + { + int len = 0; + while(len < 3 && index + len < text.Length && char.IsNumber(text[index + len])) + len++; + + byte v = 0; + while(len > 0 && !byte.TryParse(text.Substring(index, len), out v)) + len--; + + if(len == 0) + v = 0; + xlen = len; + return v; + } + + /// + /// Replace XTerm color codes into real XTerm or ANSI values. + /// + /// Text to search in. + /// Replace into XTerm codes or if false then convert to closest regular ANSI. + /// + public static string ReplaceXTerm(string text, bool fromRaw, bool allowXTerm) + { + if(fromRaw == false) + { + int index = -1; + while((index = text.IndexOf("@x")) != -1) + { + int len; + byte code = GetXTerm(text, index, out len); + text = text.Remove(index, len + 2); + if(allowXTerm) + text = text.Insert(index, "[38;5;" + code.ToString() + "m"); + else + text = text.Insert(index, "@" + XTermToANSI(code)); + } + while((index = text.IndexOf("@z")) != -1) + { + int len; + byte code = GetXTerm(text, index, out len); + text = text.Remove(index, len + 2); + if(allowXTerm) + text = text.Insert(index, "[48;5;" + code.ToString() + "m"); + else + text = text.Insert(index, "@f" + XTermToANSI(code)); + } + } + else + { + int index = -1; + while((index = text.IndexOf("[38;5;")) != -1) + { + int len; + byte c = GetXTermCode(text, index + 7, out len); + string code = c.ToString(); + if(code.Length == 1) + code = "00" + code; + else if(code.Length == 2) + code = "0" + code; + text = text.Remove(index, len + 8); + if(allowXTerm) + text = text.Insert(index, "@x" + code); + else + text = text.Insert(index, "@" + XTermToANSI(c)); + } + while((index = text.IndexOf("[48;5;")) != -1) + { + int len; + byte c = GetXTermCode(text, index + 7, out len); + string code = c.ToString(); + if(code.Length == 1) + code = "00" + code; + else if(code.Length == 2) + code = "0" + code; + text = text.Remove(index, len + 8); + if(allowXTerm) + text = text.Insert(index, "@z" + code); + else + text = text.Insert(index, "@" + XTermToANSI(c)); + } + } + return text; + } + + private static readonly char[] XTermToAnsiTable = new char[256]; + + /// + /// Convert XTerm to ansi char (closest match). + /// + /// Code to convert. + /// + public static char XTermToANSI(byte code) + { + return XTermToAnsiTable[(int)code]; + } + } + + internal class ColorEntry + { + internal ColorEntry(char colorChar, string ansi, string foreansi, string name, string hex) + { + ColorChar = colorChar; + ANSI = ansi; + ForeANSI = foreansi; + Name = name; + HEX = hex; + } + + public readonly char ColorChar; + public readonly string ANSI; + public readonly string ForeANSI; + public readonly string Name; + public readonly string HEX; + } +} diff --git a/ProxyCore/Utility/.svn/text-base/Config.cs.svn-base b/ProxyCore/Utility/.svn/text-base/Config.cs.svn-base new file mode 100644 index 0000000..e358018 --- /dev/null +++ b/ProxyCore/Utility/.svn/text-base/Config.cs.svn-base @@ -0,0 +1,333 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Text.RegularExpressions; +using System.Globalization; + +namespace ProxyCore +{ + public class ConfigFile + { + protected ConfigFile() + { + + } + + private string Filepath; + + /// + /// Did we successfully load the config file? If not then it was probably missing. + /// + public bool DidLoad + { + get; + private set; + } + + /// + /// Load a configuration file. + /// + /// Configuration name - for example "server". This should be your plugin name. + /// + internal void Load(string fileName) + { + fileName = "config." + fileName + ".txt"; + _cfgData.Clear(); + Filepath = fileName; + OnCreated(); + + if(_cfgData.Count == 0) + return; + + StreamReader f = null; + try + { + f = new StreamReader(fileName); + } + catch(FileNotFoundException) + { + SaveNew(); + return; + } + catch + { + return; + } + + string l; + while((l = f.ReadLine()) != null) + { + l = l.Trim(); + if(string.IsNullOrEmpty(l) || l.StartsWith("#") || l.StartsWith(";") || l.StartsWith("//")) + continue; + + Match m = _loadRegex.Match(l); + if(!m.Success) + continue; + + string Key = m.Groups[1].Value.ToLower(); + if(!_cfgData.ContainsKey(Key)) + continue; + + string Value = m.Groups[3].Value; + if(Value.Contains('"') && _cfgData[Key].DefaultValue is string) + Value = Value.Substring(1, Value.Length - 2); + + if(_cfgData[Key].DefaultValue is int) + { + int i; + if(!int.TryParse(Value, out i)) + continue; + + _cfgData[Key].Value = i; + } + else if(_cfgData[Key].DefaultValue is uint) + { + uint i; + if(!uint.TryParse(Value, out i)) + continue; + + _cfgData[Key].Value = i; + } + else if(_cfgData[Key].DefaultValue is long) + { + long i; + if(!long.TryParse(Value, out i)) + continue; + + _cfgData[Key].Value = i; + } + else if(_cfgData[Key].DefaultValue is ulong) + { + ulong i; + if(!ulong.TryParse(Value, out i)) + continue; + + _cfgData[Key].Value = i; + } + else if(_cfgData[Key].DefaultValue is string) + { + _cfgData[Key].Value = Value; + } + else if(_cfgData[Key].DefaultValue is float) + { + try + { + float i = Convert.ToSingle(Value, CultureInfo.InvariantCulture.NumberFormat); + _cfgData[Key].Value = i; + } + catch + { + continue; + } + } + else if(_cfgData[Key].DefaultValue is double) + { + try + { + double i = Convert.ToDouble(Value, CultureInfo.InvariantCulture.NumberFormat); + _cfgData[Key].Value = i; + } + catch + { + continue; + } + } + } + + f.Close(); + } + + /// + /// Regex pattern used to load a line from config file. + /// Groups[1]: Name of setting + /// Groups[3]: Value of setting (including "" if string) + /// + private static readonly Regex _loadRegex = new Regex("([\\w\\.]+)\\s*(=|:|-)\\s*((\".*\")|(-?\\d+\\.\\d+)|(-?\\d+))", RegexOptions.Compiled); + + /// + /// Save a new configuration file with default values. If there is an existing config file, it will be replaced. + /// + public void SaveNew() + { + if(string.IsNullOrEmpty(Filepath) || _cfgData.Count == 0) + return; + + StreamWriter f = null; + try + { + f = new StreamWriter(Filepath, false); + } + catch + { + return; + } + + foreach(KeyValuePair x in _cfgData) + { + f.WriteLine("###############################################################################"); + f.WriteLine("#"); + f.WriteLine("# " + x.Value.Key); + if(!string.IsNullOrEmpty(x.Value.Desc)) + { + string[] oDesc = Utility.WrapColored(x.Value.Desc, 70, 0); + for(int i = 0; i < oDesc.Length; i++) + f.WriteLine("# " + oDesc[i]); + } + f.WriteLine("#"); + f.WriteLine("###############################################################################"); + f.WriteLine(""); + f.WriteLine(x.Value.Key + " = " + ((x.Value.DefaultValue is string) ? ("\"" + x.Value.DefaultValue + "\"") : x.Value.DefaultValue.ToString().Replace(",", "."))); + f.WriteLine(""); + } + + f.Close(); + } + + /// + /// This will be called to populate config data with default values and descriptions. + /// + protected virtual void OnCreated() + { + } + + /// + /// Create a new setting for the file. If setting with this name already exists then skip. + /// + /// Setting name. + /// Default value for setting. Make sure to use type casting if not integer. + /// Description to write in the file for this setting. + protected void CreateSetting(string Key, object Value, string Desc) + { + if(_cfgData.ContainsKey(Key.ToLower().Trim())) + return; + + CfgEntry e = new CfgEntry() + { + Key = Key, + Value = Value, + DefaultValue = Value, + Desc = Desc + }; + + _cfgData[Key.ToLower().Trim()] = e; + } + + /// + /// Read a 32 bit integer value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public int GetInt32(string Key, int Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is int)) + return Default; + + return (int)_cfgData[Key].Value; + } + + /// + /// Read a 32 bit unsigned integer value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public uint GetUInt32(string Key, uint Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is uint)) + return Default; + + return (uint)_cfgData[Key].Value; + } + + /// + /// Read a 64 bit integer value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public long GetInt64(string Key, long Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is long)) + return Default; + + return (long)_cfgData[Key].Value; + } + + /// + /// Read a 64 bit unsigned integer value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public ulong GetUInt64(string Key, ulong Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is ulong)) + return Default; + + return (ulong)_cfgData[Key].Value; + } + + /// + /// Read a float value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public float GetFloat(string Key, float Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is float)) + return Default; + + return (float)_cfgData[Key].Value; + } + + /// + /// Read a double value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public double GetDouble(string Key, double Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is double)) + return Default; + + return (double)_cfgData[Key].Value; + } + + /// + /// Read a string value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public string GetString(string Key, string Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is string)) + return Default; + + return (string)_cfgData[Key].Value; + } + + private Dictionary _cfgData = new Dictionary(); + + private class CfgEntry + { + public string Key; + public object Value; + public object DefaultValue; + public string Desc; + } + } +} diff --git a/ProxyCore/Utility/.svn/text-base/JSON.cs.svn-base b/ProxyCore/Utility/.svn/text-base/JSON.cs.svn-base new file mode 100644 index 0000000..f13972c --- /dev/null +++ b/ProxyCore/Utility/.svn/text-base/JSON.cs.svn-base @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Jayrock.Json; +using Jayrock.Json.Conversion; + +namespace ProxyCore +{ + internal static class JSON + { + internal static bool Parse(string msg, string module, List> data) + { + try + { + JsonObject obj = (JsonObject)JsonConvert.Import(msg); + Explore(module, obj, data); + } + catch + { + //data.Clear(); + return false; + } + return true; + } + + private static void Explore(string nameSpace, JsonObject obj, List> x) + { + var keys = obj.Names; + foreach(string i in keys) + ExplorePair(nameSpace + "." + i, obj[i], x); + } + + private static void ExplorePair(string nameSpace, object obj, List> x) + { + if((obj is JsonNumber) || + (obj is JsonNull) || + (obj is string) || + (obj is JsonBoolean) || + (obj is JsonString)) + x.Add(new KeyValuePair(nameSpace, obj.ToString())); + else if(obj is JsonArray) + { + foreach(JsonObject i in (JsonArray)obj) + Explore(nameSpace, i, x); + } + else if(obj is JsonObject) + Explore(nameSpace, (JsonObject)obj, x); + else if(obj == null) + x.Add(new KeyValuePair(nameSpace, "null")); + else throw new Exception(); + } + } +} diff --git a/ProxyCore/Utility/.svn/text-base/ServerConfig.cs.svn-base b/ProxyCore/Utility/.svn/text-base/ServerConfig.cs.svn-base new file mode 100644 index 0000000..4bd043f --- /dev/null +++ b/ProxyCore/Utility/.svn/text-base/ServerConfig.cs.svn-base @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore; + +namespace ProxyCore +{ + public class ServerConfig : ConfigFile + { + public ServerConfig() + { + Load("server"); + } + + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Listen.Address", "127.0.0.1", "This is the address that the proxy program will be listening on. You will have to connect to this address with your MUD client. Enter 127.0.0.1 to listen only on current computer, enter LAN IP to listen only in your home network or enter 0.0.0.0 to listen on all addresses (even remote)."); + CreateSetting("Listen.Port", 4000, "This is the port that the proxy program will be listening on. You will have to enter this port in your MUD client. If you want remote connections you may have to open this port (TCP) in your firewall."); + CreateSetting("MUD.Address", "aardmud.org", "This is the address for MUD connection."); + CreateSetting("MUD.Port", 4000, "This is the port for MUD connection."); + CreateSetting("Passwords", "", "Passwords for the proxy and their user levels. For example: \"abc->1,def->2,ghi321->1\". If user enters def as password they will get user level 2. Enter as many passwords as you like. If you leave this empty, the proxy will not ask for a password and user will be authed with level 1. The password values should be between 1 and 64."); + CreateSetting("GMCP.Supports", "Core=1, Char=1, Room=1, Comm=1", "What GMCP options do we have on by default? These are modules only needed for the proxy. If your client needs another module it will enable it itself - no need to change here."); + CreateSetting("AutoConnect", 0, "Proxy program will always automatically (re)connect to MUD if there is a client online in the proxy. If there is no client online the proxy will not automatically connect. Enabling this option will force the proxy to always auto (re)connect."); + CreateSetting("ClientCompression", 1, "Enable compression for connected clients (MUD compression will always be on, even if you disable this option). Don't disable unless you are having problems with compression and can't disable client side."); + } + } + + public class CoreConfig : ConfigFile + { + public CoreConfig() + { + Load("core"); + } + + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Updates.Core", 1, "Automatically check updates for core."); + CreateSetting("Updates.Plugins", 1, "Automatically check updates for plugins."); + } + } +} diff --git a/ProxyCore/Utility/.svn/text-base/Wrap.cs.svn-base b/ProxyCore/Utility/.svn/text-base/Wrap.cs.svn-base new file mode 100644 index 0000000..b73ed1a --- /dev/null +++ b/ProxyCore/Utility/.svn/text-base/Wrap.cs.svn-base @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ProxyCore +{ + public partial class Utility + { + public static string[] WrapColored(string text, int maxLength, int IndentSize) + { + if(maxLength <= 2) + return null; + + if(text.Length <= maxLength) + return new string[1] { text }; + + StringBuilder wrapBuilder = new StringBuilder(); + int count = 0; + bool printed = false; + int lastOk = 0; + int realLength = 0; + int fakeIndex = 0; + text = text.TrimEnd(); + bool didAt = false; + bool doNow = false; + for(int i = 0; i < text.Length; i++) + { + switch(text[i]) + { + case ' ': + /*case ',': + case '.': + case '?': + case '!': + case ':': + case ';': + case ')': + case ']': + case '}': + case '-':*/ + case '\t': + lastOk = i; + realLength++; + break; + + case '\n': + case '\r': + doNow = true; + lastOk = i; + realLength++; + break; + + case '@': + if(didAt) + realLength++; + didAt = !didAt; + break; + + default: + if(!didAt) + realLength++; + else + didAt = false; + break; + } + + if(realLength >= maxLength || doNow) + { + if(printed && IndentSize > 0) + wrapBuilder.Append(' ', IndentSize); + if(lastOk > 0) + { + wrapBuilder.Append(text.Substring(fakeIndex, lastOk - fakeIndex + 1).TrimEnd() + Environment.NewLine); + i = lastOk; + } + else + wrapBuilder.Append(text.Substring(fakeIndex, i - fakeIndex + 1).TrimEnd() + Environment.NewLine); + + fakeIndex = lastOk > 0 ? lastOk + 1 : i + 1; + lastOk = 0; + realLength = IndentSize; + printed = true; + count++; + doNow = false; + } + } + + if(fakeIndex < text.Length - 1) + { + if(printed && IndentSize > 0) + wrapBuilder.Append(' ', IndentSize); + wrapBuilder.Append(text.Substring(fakeIndex, text.Length - fakeIndex).TrimEnd() + Environment.NewLine); + count++; + } + + List lp = wrapBuilder.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList(); + lp.RemoveAt(lp.Count - 1); + return lp.ToArray(); + } + + public static string FormatColoredString(string msg, int len) + { + StringBuilder str = new StringBuilder(); + int real = 0; + bool at = false; + for(int i = 0; i < msg.Length; i++) + { + if(msg[i] == '@') + { + if(at) + { + real++; + str.Append("@@"); + } + at = !at; + } + else if(at) + { + str.Append("@" + msg[i].ToString()); + at = false; + } + else + { + str.Append(msg[i]); + real++; + } + } + + if(real < Math.Abs(len)) + { + if(len < 0) + str.Append(' ', Math.Abs(len) - real); + else + str.Insert(0, " ", len - real); + } + + return str.ToString(); + } + } +} diff --git a/ProxyCore/Utility/.svn/text-base/desktop.ini b/ProxyCore/Utility/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Utility/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Utility/.svn/tmp/desktop.ini b/ProxyCore/Utility/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Utility/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Utility/.svn/tmp/prop-base/desktop.ini b/ProxyCore/Utility/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Utility/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Utility/.svn/tmp/props/desktop.ini b/ProxyCore/Utility/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Utility/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Utility/.svn/tmp/text-base/desktop.ini b/ProxyCore/Utility/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Utility/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/Utility/Colors.cs b/ProxyCore/Utility/Colors.cs new file mode 100644 index 0000000..1498ab1 --- /dev/null +++ b/ProxyCore/Utility/Colors.cs @@ -0,0 +1,748 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ProxyCore +{ + public static class Colors + { + static Colors() + { + XTermToAnsiTable[0] = 'w'; + XTermToAnsiTable[1] = 'r'; + XTermToAnsiTable[2] = 'g'; + XTermToAnsiTable[3] = 'y'; + XTermToAnsiTable[4] = 'b'; + XTermToAnsiTable[5] = 'm'; + XTermToAnsiTable[6] = 'c'; + XTermToAnsiTable[7] = 'w'; + XTermToAnsiTable[8] = 'D'; + XTermToAnsiTable[9] = 'R'; + XTermToAnsiTable[10] = 'G'; + XTermToAnsiTable[11] = 'Y'; + XTermToAnsiTable[12] = 'B'; + XTermToAnsiTable[13] = 'M'; + XTermToAnsiTable[14] = 'C'; + XTermToAnsiTable[15] = 'W'; + XTermToAnsiTable[16] = 'w'; + XTermToAnsiTable[17] = 'b'; + XTermToAnsiTable[18] = 'b'; + XTermToAnsiTable[19] = 'b'; + XTermToAnsiTable[20] = 'B'; + XTermToAnsiTable[21] = 'B'; + XTermToAnsiTable[22] = 'g'; + XTermToAnsiTable[23] = 'c'; + XTermToAnsiTable[24] = 'c'; + XTermToAnsiTable[25] = 'c'; + XTermToAnsiTable[26] = 'c'; + XTermToAnsiTable[27] = 'B'; + XTermToAnsiTable[28] = 'g'; + XTermToAnsiTable[29] = 'c'; + XTermToAnsiTable[30] = 'c'; + XTermToAnsiTable[31] = 'c'; + XTermToAnsiTable[32] = 'c'; + XTermToAnsiTable[33] = 'C'; + XTermToAnsiTable[34] = 'g'; + XTermToAnsiTable[35] = 'c'; + XTermToAnsiTable[36] = 'c'; + XTermToAnsiTable[37] = 'c'; + XTermToAnsiTable[38] = 'C'; + XTermToAnsiTable[39] = 'C'; + XTermToAnsiTable[40] = 'G'; + XTermToAnsiTable[41] = 'c'; + XTermToAnsiTable[42] = 'c'; + XTermToAnsiTable[43] = 'C'; + XTermToAnsiTable[44] = 'C'; + XTermToAnsiTable[45] = 'C'; + XTermToAnsiTable[46] = 'G'; + XTermToAnsiTable[47] = 'G'; + XTermToAnsiTable[48] = 'C'; + XTermToAnsiTable[49] = 'C'; + XTermToAnsiTable[50] = 'C'; + XTermToAnsiTable[51] = 'C'; + XTermToAnsiTable[52] = 'r'; + XTermToAnsiTable[53] = 'm'; + XTermToAnsiTable[54] = 'm'; + XTermToAnsiTable[55] = 'm'; + XTermToAnsiTable[56] = 'm'; + XTermToAnsiTable[57] = 'B'; + XTermToAnsiTable[58] = 'y'; + XTermToAnsiTable[59] = 'D'; + XTermToAnsiTable[60] = 'D'; + XTermToAnsiTable[61] = 'D'; + XTermToAnsiTable[62] = 'D'; + XTermToAnsiTable[63] = 'B'; + XTermToAnsiTable[64] = 'y'; + XTermToAnsiTable[65] = 'D'; + XTermToAnsiTable[66] = 'D'; + XTermToAnsiTable[67] = 'D'; + XTermToAnsiTable[68] = 'D'; + XTermToAnsiTable[69] = 'w'; + XTermToAnsiTable[70] = 'y'; + XTermToAnsiTable[71] = 'D'; + XTermToAnsiTable[72] = 'D'; + XTermToAnsiTable[73] = 'D'; + XTermToAnsiTable[74] = 'w'; + XTermToAnsiTable[75] = 'w'; + XTermToAnsiTable[76] = 'y'; + XTermToAnsiTable[77] = 'D'; + XTermToAnsiTable[78] = 'D'; + XTermToAnsiTable[79] = 'w'; + XTermToAnsiTable[80] = 'w'; + XTermToAnsiTable[81] = 'C'; + XTermToAnsiTable[82] = 'G'; + XTermToAnsiTable[83] = 'G'; + XTermToAnsiTable[84] = 'w'; + XTermToAnsiTable[85] = 'w'; + XTermToAnsiTable[86] = 'C'; + XTermToAnsiTable[87] = 'C'; + XTermToAnsiTable[88] = 'r'; + XTermToAnsiTable[89] = 'm'; + XTermToAnsiTable[90] = 'm'; + XTermToAnsiTable[91] = 'm'; + XTermToAnsiTable[92] = 'm'; + XTermToAnsiTable[93] = 'M'; + XTermToAnsiTable[94] = 'y'; + XTermToAnsiTable[95] = 'D'; + XTermToAnsiTable[96] = 'D'; + XTermToAnsiTable[97] = 'D'; + XTermToAnsiTable[98] = 'D'; + XTermToAnsiTable[99] = 'w'; + XTermToAnsiTable[100] = 'y'; + XTermToAnsiTable[101] = 'D'; + XTermToAnsiTable[102] = 'D'; + XTermToAnsiTable[103] = 'D'; + XTermToAnsiTable[104] = 'w'; + XTermToAnsiTable[105] = 'w'; + XTermToAnsiTable[106] = 'y'; + XTermToAnsiTable[107] = 'D'; + XTermToAnsiTable[108] = 'D'; + XTermToAnsiTable[109] = 'w'; + XTermToAnsiTable[110] = 'w'; + XTermToAnsiTable[111] = 'w'; + XTermToAnsiTable[112] = 'y'; + XTermToAnsiTable[113] = 'D'; + XTermToAnsiTable[114] = 'w'; + XTermToAnsiTable[115] = 'w'; + XTermToAnsiTable[116] = 'w'; + XTermToAnsiTable[117] = 'w'; + XTermToAnsiTable[118] = 'Y'; + XTermToAnsiTable[119] = 'w'; + XTermToAnsiTable[120] = 'w'; + XTermToAnsiTable[121] = 'w'; + XTermToAnsiTable[122] = 'w'; + XTermToAnsiTable[123] = 'w'; + XTermToAnsiTable[124] = 'r'; + XTermToAnsiTable[125] = 'm'; + XTermToAnsiTable[126] = 'm'; + XTermToAnsiTable[127] = 'm'; + XTermToAnsiTable[128] = 'M'; + XTermToAnsiTable[129] = 'M'; + XTermToAnsiTable[130] = 'y'; + XTermToAnsiTable[131] = 'D'; + XTermToAnsiTable[132] = 'D'; + XTermToAnsiTable[133] = 'D'; + XTermToAnsiTable[134] = 'w'; + XTermToAnsiTable[135] = 'w'; + XTermToAnsiTable[136] = 'y'; + XTermToAnsiTable[137] = 'D'; + XTermToAnsiTable[138] = 'D'; + XTermToAnsiTable[139] = 'w'; + XTermToAnsiTable[140] = 'w'; + XTermToAnsiTable[141] = 'w'; + XTermToAnsiTable[142] = 'y'; + XTermToAnsiTable[143] = 'D'; + XTermToAnsiTable[144] = 'w'; + XTermToAnsiTable[145] = 'w'; + XTermToAnsiTable[146] = 'w'; + XTermToAnsiTable[147] = 'w'; + XTermToAnsiTable[148] = 'Y'; + XTermToAnsiTable[149] = 'w'; + XTermToAnsiTable[150] = 'w'; + XTermToAnsiTable[151] = 'w'; + XTermToAnsiTable[152] = 'w'; + XTermToAnsiTable[153] = 'w'; + XTermToAnsiTable[154] = 'Y'; + XTermToAnsiTable[155] = 'w'; + XTermToAnsiTable[156] = 'w'; + XTermToAnsiTable[157] = 'w'; + XTermToAnsiTable[158] = 'w'; + XTermToAnsiTable[159] = 'W'; + XTermToAnsiTable[160] = 'R'; + XTermToAnsiTable[161] = 'm'; + XTermToAnsiTable[162] = 'm'; + XTermToAnsiTable[163] = 'M'; + XTermToAnsiTable[164] = 'M'; + XTermToAnsiTable[165] = 'M'; + XTermToAnsiTable[166] = 'y'; + XTermToAnsiTable[167] = 'D'; + XTermToAnsiTable[168] = 'D'; + XTermToAnsiTable[169] = 'w'; + XTermToAnsiTable[170] = 'w'; + XTermToAnsiTable[171] = 'M'; + XTermToAnsiTable[172] = 'y'; + XTermToAnsiTable[173] = 'D'; + XTermToAnsiTable[174] = 'w'; + XTermToAnsiTable[175] = 'w'; + XTermToAnsiTable[176] = 'w'; + XTermToAnsiTable[177] = 'w'; + XTermToAnsiTable[178] = 'Y'; + XTermToAnsiTable[179] = 'w'; + XTermToAnsiTable[180] = 'w'; + XTermToAnsiTable[181] = 'w'; + XTermToAnsiTable[182] = 'w'; + XTermToAnsiTable[183] = 'w'; + XTermToAnsiTable[184] = 'Y'; + XTermToAnsiTable[185] = 'w'; + XTermToAnsiTable[186] = 'w'; + XTermToAnsiTable[187] = 'w'; + XTermToAnsiTable[188] = 'w'; + XTermToAnsiTable[189] = 'W'; + XTermToAnsiTable[190] = 'Y'; + XTermToAnsiTable[191] = 'Y'; + XTermToAnsiTable[192] = 'w'; + XTermToAnsiTable[193] = 'w'; + XTermToAnsiTable[194] = 'W'; + XTermToAnsiTable[195] = 'W'; + XTermToAnsiTable[196] = 'R'; + XTermToAnsiTable[197] = 'R'; + XTermToAnsiTable[198] = 'M'; + XTermToAnsiTable[199] = 'M'; + XTermToAnsiTable[200] = 'M'; + XTermToAnsiTable[201] = 'M'; + XTermToAnsiTable[202] = 'R'; + XTermToAnsiTable[203] = 'R'; + XTermToAnsiTable[204] = 'w'; + XTermToAnsiTable[205] = 'w'; + XTermToAnsiTable[206] = 'M'; + XTermToAnsiTable[207] = 'M'; + XTermToAnsiTable[208] = 'Y'; + XTermToAnsiTable[209] = 'w'; + XTermToAnsiTable[210] = 'w'; + XTermToAnsiTable[211] = 'w'; + XTermToAnsiTable[212] = 'w'; + XTermToAnsiTable[213] = 'w'; + XTermToAnsiTable[214] = 'Y'; + XTermToAnsiTable[215] = 'w'; + XTermToAnsiTable[216] = 'w'; + XTermToAnsiTable[217] = 'w'; + XTermToAnsiTable[218] = 'w'; + XTermToAnsiTable[219] = 'W'; + XTermToAnsiTable[220] = 'Y'; + XTermToAnsiTable[221] = 'Y'; + XTermToAnsiTable[222] = 'w'; + XTermToAnsiTable[223] = 'w'; + XTermToAnsiTable[224] = 'W'; + XTermToAnsiTable[225] = 'W'; + XTermToAnsiTable[226] = 'Y'; + XTermToAnsiTable[227] = 'Y'; + XTermToAnsiTable[228] = 'w'; + XTermToAnsiTable[229] = 'W'; + XTermToAnsiTable[230] = 'W'; + XTermToAnsiTable[231] = 'W'; + XTermToAnsiTable[232] = 'w'; + XTermToAnsiTable[233] = 'w'; + XTermToAnsiTable[234] = 'w'; + XTermToAnsiTable[235] = 'w'; + XTermToAnsiTable[236] = 'w'; + XTermToAnsiTable[237] = 'w'; + XTermToAnsiTable[238] = 'D'; + XTermToAnsiTable[239] = 'D'; + XTermToAnsiTable[240] = 'D'; + XTermToAnsiTable[241] = 'D'; + XTermToAnsiTable[242] = 'D'; + XTermToAnsiTable[243] = 'D'; + XTermToAnsiTable[244] = 'D'; + XTermToAnsiTable[245] = 'D'; + XTermToAnsiTable[246] = 'D'; + XTermToAnsiTable[247] = 'D'; + XTermToAnsiTable[248] = 'w'; + XTermToAnsiTable[249] = 'w'; + XTermToAnsiTable[250] = 'w'; + XTermToAnsiTable[251] = 'w'; + XTermToAnsiTable[252] = 'w'; + XTermToAnsiTable[253] = 'w'; + XTermToAnsiTable[254] = 'W'; + XTermToAnsiTable[255] = 'W'; + } + + /// + /// Check if a char code is ANSI color code. + /// + /// Char to check for. + /// + public static bool IsColorCode(char code) + { + // Don't allow black color, we can't see it + if(code == 'd') + return false; + + foreach(ColorEntry x in ColorEntries) + { + if(x.ColorChar == code) + return true; + } + + return false; + } + + /// + /// Convert normal colors to HTML colors. It converts inserting </font> in front of every color too + /// so you need to have started with some font already. + /// + /// Message to convert colors in. + /// + public static string GetHTMLColors(string Msg) + { + Msg = Msg.Replace("@@", colorEscape); + foreach(ColorEntry c in ColorEntries) + Msg = Msg.Replace("@" + c.ColorChar, ""); + Msg = Msg.Replace(colorEscape, "@"); + return Msg; + } + + // Any codes that aren't xterm and aren't listed here will be @e instead of escape char. + private static ColorEntry[] ColorEntries = new[] + { + new ColorEntry('w', "", "", "grey", "C0C0C0"), + new ColorEntry('W', "", "", "white", "FFFFFF"), + new ColorEntry('G', "", "", "green", "00FF00"), + new ColorEntry('g', "", "", "dark green", "008000"), + new ColorEntry('R', "", "", "red", "FF0000"), + new ColorEntry('r', "", "", "dark red", "800000"), + new ColorEntry('Y', "", "", "yellow", "FFFF00"), + new ColorEntry('y', "", "", "dark yellow", "808000"), + new ColorEntry('C', "", "", "cyan", "00FFFF"), + new ColorEntry('c', "", "", "dark cyan", "008080"), + new ColorEntry('B', "", "", "blue", "0000FF"), + new ColorEntry('b', "", "", "dark blue", "000080"), + new ColorEntry('M', "", "", "magenta", "FF00FF"), + new ColorEntry('m', "", "", "dark magenta", "800080"), + new ColorEntry('D', "", "", "dark grey", "808080"), + new ColorEntry('d', "", "", "black", "000000"), + new ColorEntry('q', "", "", "default", "C0C0C0") + }; + + /// + /// Get last color code in the string (not including foreground colors). This will return + /// with the @ sign. + /// + /// + /// + public static string GetLastColorCode(string text) + { + text = text.Replace("@@", colorEscape); + + // Start checking from the end + for(int i = text.Length - 1; i >= 0; i--) + { + if(text[i] == '@' && i + 1 < text.Length && text[i + 1] != 'f' && text[i + 1] != 'y') + { + if(text[i + 1] == 'x') + { + int len; + string code = GetXTerm(text, i, out len).ToString(); + if(len == 0) + continue; + + if(code.Length == 1) + code = "00" + code; + else if(code.Length == 2) + code = "0" + code; + + return "@x" + code; + } + else + return "@" + text[i + 1]; + } + } + + return string.Empty; + } + + /// + /// Removes duplicate colors from a string. For example "@r @Y bla@w" would become " @Ybla". + /// Also XTERM color codes become padded with zero if they are present. + /// + /// String to fix colors in. + /// + public static string RemoveDuplicateColors(string orig) + { + StringBuilder txt = new StringBuilder(); + + string currentColor = ""; + bool didWriteColor = false; + bool hasAt = false; + bool hasE = false; + + for(int i = 0; i < orig.Length; i++) + { + switch(orig[i]) + { + case '@': + { + if(hasAt) + { + if(!didWriteColor) + { + txt.Append(currentColor); + didWriteColor = true; + } + + txt.Append("@@"); + hasAt = false; + } + else + { + hasAt = true; + } + } break; + + case ' ': + case '\r': + case '\n': + case '\t': + txt.Append(orig[i]); + break; + + default: + { + if(hasAt) + { + if(orig[i] == 'e') + { + txt.Append("@e"); + hasAt = false; + hasE = true; + continue; + } + if(orig[i] == 'f') + { + if(i + 1 < orig.Length) + txt.Append("@f" + orig[i + 1]); + i++; + hasAt = false; + continue; + } + if(orig[i] == 'z') + { + int len; + string code = GetXTerm(orig, i - 1, out len).ToString(); + if(len > 0) + { + if(code.Length == 1) + code = "00" + code; + else if(code.Length == 2) + code = "0" + code; + txt.Append("@z" + code); + i += len; + } + hasAt = false; + continue; + } + if(orig[i] == 'x') + { + int len; + string code = GetXTerm(orig, i - 1, out len).ToString(); + if(len > 0) + { + if(code.Length == 1) + code = "00" + code; + else if(code.Length == 2) + code = "0" + code; + i += len; + if(currentColor != "@x" + code) + { + currentColor = "@x" + code; + didWriteColor = false; + } + } + hasAt = false; + continue; + } + if(currentColor != "@" + orig[i]) + { + currentColor = "@" + orig[i]; + didWriteColor = false; + } + hasAt = false; + } + else if(hasE) + { + txt.Append(orig[i]); + if(orig[i] == 'm') + hasE = false; + } + else + { + if(!didWriteColor) + { + txt.Append(currentColor); + didWriteColor = true; + } + + txt.Append(orig[i]); + } + } break; + } + } + + return txt.ToString(); + } + + /// + /// This is what we turn the @ into so we can replace colors and keep the symbol intact + /// + private const string colorEscape = "#\r\ncolor_escape_sequence\r\n#"; + + /// + /// This function will turn either ANSI color to our format or vice versa. + /// + /// String passed for color changing. + /// False means we change our format (@x) to ANSI; true means we change ANSI to our format (we don't convert XTERM colors this way though). + /// Allow replacing xterm colors? If disabled we will replace into closest ANSI. + /// + public static string FixColors(string text, bool fromRaw, bool allowXTerm) + { + if(fromRaw == false) + { + text = text.Replace("@@", colorEscape); + text = ReplaceXTerm(text, fromRaw, allowXTerm); + for(int i = 0; i < ColorEntries.Length; i++) + { + text = text.Replace("@" + ColorEntries[i].ColorChar, ColorEntries[i].ANSI); + text = text.Replace("@f" + ColorEntries[i].ColorChar, ColorEntries[i].ForeANSI); + } + text = text.Replace("@e", ""); + text = text.Replace(colorEscape, "@"); + } + else + { + text = text.Replace("@", colorEscape); + text = ReplaceXTerm(text, fromRaw, allowXTerm); + for(int i = 0; i < ColorEntries.Length; i++) + { + text = text.Replace(ColorEntries[i].ANSI, "@" + ColorEntries[i].ColorChar); + text = text.Replace(ColorEntries[i].ForeANSI, "@f" + ColorEntries[i].ColorChar); + } + text = text.Replace("", "@e"); + text = text.Replace(colorEscape, "@@"); + } + return text; + } + + /// + /// Remove dark grey color code from string and replace it with normal grey. + /// + /// String to remove dark grey from (replacing it with normal grey). + /// + public static string RemoveGray(string text) + { + text = text.Replace("@@", colorEscape); + text = text.Replace("@D", "@w"); + text = text.Replace("@fD", "@fw"); + text = text.Replace(colorEscape, "@@"); + return text; + } + + /// + /// This function will Remove all color from the string. + /// + /// String where the color is being removed. + /// True means raw color (ansi); false means our format (@x). + /// + public static string RemoveColors(string text, bool raw) + { + if(!raw) + { + text = text.Replace("@@", colorEscape); + for(int i = 0; i < ColorEntries.Length; i++) + { + text = text.Replace("@" + ColorEntries[i].ColorChar, ""); + text = text.Replace("@f" + ColorEntries[i].ColorChar, ""); + } + int j; + while((j = text.IndexOf("@x")) != -1 || (j = text.IndexOf("@z")) != -1) + text = text.Remove(j, 5); + text = text.Replace(colorEscape, "@"); + return text; + } + + for(int i = 0; i < ColorEntries.Length; i++) + { + text = text.Replace(ColorEntries[i].ANSI, ""); + text = text.Replace(ColorEntries[i].ForeANSI, ""); + } + + int k; + while((k = text.IndexOf("")) != -1) + { + int l = text.IndexOf('m', k); + if(l == -1) + break; + text = text.Remove(k, l - k); + } + return text; + } + + /// + /// Get XTerm color code from text in index (index must be at @x or @z, before it). + /// + /// Text to get color code from. + /// Index of @z or @x. + /// + public static byte GetXTerm(string text, int index, out int xlen) + { + // Skip @x or @z + index += 2; + + // Get byte number now + byte v = GetXTermCode(text, index, out xlen); + return v; + } + + /// + /// Get full escape string from string in index. + /// + /// Text to search from. + /// Index in text of @e + /// Length of the thing behind @e. + /// + public static string GetEscape(string text, int index, out int xlen) + { + index += 2; + if((xlen = text.IndexOf('m', index)) == -1) + { + xlen = 0; + return "@e[0m"; + } + + xlen++; // include the 'm' + xlen -= index; + return "@e" + text.Substring(index, xlen); + } + + /// + /// Get XTerm color code from text in index (index must be at the number not before @x or @z like the other function) + /// + /// Text to get color code from. + /// Index of number. + /// Length of the number. + /// + public static byte GetXTermCode(string text, int index, out int xlen) + { + int len = 0; + while(len < 3 && index + len < text.Length && char.IsNumber(text[index + len])) + len++; + + byte v = 0; + while(len > 0 && !byte.TryParse(text.Substring(index, len), out v)) + len--; + + if(len == 0) + v = 0; + xlen = len; + return v; + } + + /// + /// Replace XTerm color codes into real XTerm or ANSI values. + /// + /// Text to search in. + /// Replace into XTerm codes or if false then convert to closest regular ANSI. + /// + public static string ReplaceXTerm(string text, bool fromRaw, bool allowXTerm) + { + if(fromRaw == false) + { + int index = -1; + while((index = text.IndexOf("@x")) != -1) + { + int len; + byte code = GetXTerm(text, index, out len); + text = text.Remove(index, len + 2); + if(allowXTerm) + text = text.Insert(index, "[38;5;" + code.ToString() + "m"); + else + text = text.Insert(index, "@" + XTermToANSI(code)); + } + while((index = text.IndexOf("@z")) != -1) + { + int len; + byte code = GetXTerm(text, index, out len); + text = text.Remove(index, len + 2); + if(allowXTerm) + text = text.Insert(index, "[48;5;" + code.ToString() + "m"); + else + text = text.Insert(index, "@f" + XTermToANSI(code)); + } + } + else + { + int index = -1; + while((index = text.IndexOf("[38;5;")) != -1) + { + int len; + byte c = GetXTermCode(text, index + 7, out len); + string code = c.ToString(); + if(code.Length == 1) + code = "00" + code; + else if(code.Length == 2) + code = "0" + code; + text = text.Remove(index, len + 8); + if(allowXTerm) + text = text.Insert(index, "@x" + code); + else + text = text.Insert(index, "@" + XTermToANSI(c)); + } + while((index = text.IndexOf("[48;5;")) != -1) + { + int len; + byte c = GetXTermCode(text, index + 7, out len); + string code = c.ToString(); + if(code.Length == 1) + code = "00" + code; + else if(code.Length == 2) + code = "0" + code; + text = text.Remove(index, len + 8); + if(allowXTerm) + text = text.Insert(index, "@z" + code); + else + text = text.Insert(index, "@" + XTermToANSI(c)); + } + } + return text; + } + + private static readonly char[] XTermToAnsiTable = new char[256]; + + /// + /// Convert XTerm to ansi char (closest match). + /// + /// Code to convert. + /// + public static char XTermToANSI(byte code) + { + return XTermToAnsiTable[(int)code]; + } + } + + internal class ColorEntry + { + internal ColorEntry(char colorChar, string ansi, string foreansi, string name, string hex) + { + ColorChar = colorChar; + ANSI = ansi; + ForeANSI = foreansi; + Name = name; + HEX = hex; + } + + public readonly char ColorChar; + public readonly string ANSI; + public readonly string ForeANSI; + public readonly string Name; + public readonly string HEX; + } +} diff --git a/ProxyCore/Utility/Config.cs b/ProxyCore/Utility/Config.cs new file mode 100644 index 0000000..e358018 --- /dev/null +++ b/ProxyCore/Utility/Config.cs @@ -0,0 +1,333 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Text.RegularExpressions; +using System.Globalization; + +namespace ProxyCore +{ + public class ConfigFile + { + protected ConfigFile() + { + + } + + private string Filepath; + + /// + /// Did we successfully load the config file? If not then it was probably missing. + /// + public bool DidLoad + { + get; + private set; + } + + /// + /// Load a configuration file. + /// + /// Configuration name - for example "server". This should be your plugin name. + /// + internal void Load(string fileName) + { + fileName = "config." + fileName + ".txt"; + _cfgData.Clear(); + Filepath = fileName; + OnCreated(); + + if(_cfgData.Count == 0) + return; + + StreamReader f = null; + try + { + f = new StreamReader(fileName); + } + catch(FileNotFoundException) + { + SaveNew(); + return; + } + catch + { + return; + } + + string l; + while((l = f.ReadLine()) != null) + { + l = l.Trim(); + if(string.IsNullOrEmpty(l) || l.StartsWith("#") || l.StartsWith(";") || l.StartsWith("//")) + continue; + + Match m = _loadRegex.Match(l); + if(!m.Success) + continue; + + string Key = m.Groups[1].Value.ToLower(); + if(!_cfgData.ContainsKey(Key)) + continue; + + string Value = m.Groups[3].Value; + if(Value.Contains('"') && _cfgData[Key].DefaultValue is string) + Value = Value.Substring(1, Value.Length - 2); + + if(_cfgData[Key].DefaultValue is int) + { + int i; + if(!int.TryParse(Value, out i)) + continue; + + _cfgData[Key].Value = i; + } + else if(_cfgData[Key].DefaultValue is uint) + { + uint i; + if(!uint.TryParse(Value, out i)) + continue; + + _cfgData[Key].Value = i; + } + else if(_cfgData[Key].DefaultValue is long) + { + long i; + if(!long.TryParse(Value, out i)) + continue; + + _cfgData[Key].Value = i; + } + else if(_cfgData[Key].DefaultValue is ulong) + { + ulong i; + if(!ulong.TryParse(Value, out i)) + continue; + + _cfgData[Key].Value = i; + } + else if(_cfgData[Key].DefaultValue is string) + { + _cfgData[Key].Value = Value; + } + else if(_cfgData[Key].DefaultValue is float) + { + try + { + float i = Convert.ToSingle(Value, CultureInfo.InvariantCulture.NumberFormat); + _cfgData[Key].Value = i; + } + catch + { + continue; + } + } + else if(_cfgData[Key].DefaultValue is double) + { + try + { + double i = Convert.ToDouble(Value, CultureInfo.InvariantCulture.NumberFormat); + _cfgData[Key].Value = i; + } + catch + { + continue; + } + } + } + + f.Close(); + } + + /// + /// Regex pattern used to load a line from config file. + /// Groups[1]: Name of setting + /// Groups[3]: Value of setting (including "" if string) + /// + private static readonly Regex _loadRegex = new Regex("([\\w\\.]+)\\s*(=|:|-)\\s*((\".*\")|(-?\\d+\\.\\d+)|(-?\\d+))", RegexOptions.Compiled); + + /// + /// Save a new configuration file with default values. If there is an existing config file, it will be replaced. + /// + public void SaveNew() + { + if(string.IsNullOrEmpty(Filepath) || _cfgData.Count == 0) + return; + + StreamWriter f = null; + try + { + f = new StreamWriter(Filepath, false); + } + catch + { + return; + } + + foreach(KeyValuePair x in _cfgData) + { + f.WriteLine("###############################################################################"); + f.WriteLine("#"); + f.WriteLine("# " + x.Value.Key); + if(!string.IsNullOrEmpty(x.Value.Desc)) + { + string[] oDesc = Utility.WrapColored(x.Value.Desc, 70, 0); + for(int i = 0; i < oDesc.Length; i++) + f.WriteLine("# " + oDesc[i]); + } + f.WriteLine("#"); + f.WriteLine("###############################################################################"); + f.WriteLine(""); + f.WriteLine(x.Value.Key + " = " + ((x.Value.DefaultValue is string) ? ("\"" + x.Value.DefaultValue + "\"") : x.Value.DefaultValue.ToString().Replace(",", "."))); + f.WriteLine(""); + } + + f.Close(); + } + + /// + /// This will be called to populate config data with default values and descriptions. + /// + protected virtual void OnCreated() + { + } + + /// + /// Create a new setting for the file. If setting with this name already exists then skip. + /// + /// Setting name. + /// Default value for setting. Make sure to use type casting if not integer. + /// Description to write in the file for this setting. + protected void CreateSetting(string Key, object Value, string Desc) + { + if(_cfgData.ContainsKey(Key.ToLower().Trim())) + return; + + CfgEntry e = new CfgEntry() + { + Key = Key, + Value = Value, + DefaultValue = Value, + Desc = Desc + }; + + _cfgData[Key.ToLower().Trim()] = e; + } + + /// + /// Read a 32 bit integer value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public int GetInt32(string Key, int Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is int)) + return Default; + + return (int)_cfgData[Key].Value; + } + + /// + /// Read a 32 bit unsigned integer value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public uint GetUInt32(string Key, uint Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is uint)) + return Default; + + return (uint)_cfgData[Key].Value; + } + + /// + /// Read a 64 bit integer value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public long GetInt64(string Key, long Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is long)) + return Default; + + return (long)_cfgData[Key].Value; + } + + /// + /// Read a 64 bit unsigned integer value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public ulong GetUInt64(string Key, ulong Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is ulong)) + return Default; + + return (ulong)_cfgData[Key].Value; + } + + /// + /// Read a float value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public float GetFloat(string Key, float Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is float)) + return Default; + + return (float)_cfgData[Key].Value; + } + + /// + /// Read a double value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public double GetDouble(string Key, double Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is double)) + return Default; + + return (double)_cfgData[Key].Value; + } + + /// + /// Read a string value from configuration file. + /// + /// Name of the option. + /// Default value if config is missing this option or is invalid. + /// + public string GetString(string Key, string Default) + { + Key = Key.ToLower().Trim(); + if(string.IsNullOrEmpty(Key) || !_cfgData.ContainsKey(Key) || !(_cfgData[Key].Value is string)) + return Default; + + return (string)_cfgData[Key].Value; + } + + private Dictionary _cfgData = new Dictionary(); + + private class CfgEntry + { + public string Key; + public object Value; + public object DefaultValue; + public string Desc; + } + } +} diff --git a/ProxyCore/Utility/JSON.cs b/ProxyCore/Utility/JSON.cs new file mode 100644 index 0000000..f13972c --- /dev/null +++ b/ProxyCore/Utility/JSON.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Jayrock.Json; +using Jayrock.Json.Conversion; + +namespace ProxyCore +{ + internal static class JSON + { + internal static bool Parse(string msg, string module, List> data) + { + try + { + JsonObject obj = (JsonObject)JsonConvert.Import(msg); + Explore(module, obj, data); + } + catch + { + //data.Clear(); + return false; + } + return true; + } + + private static void Explore(string nameSpace, JsonObject obj, List> x) + { + var keys = obj.Names; + foreach(string i in keys) + ExplorePair(nameSpace + "." + i, obj[i], x); + } + + private static void ExplorePair(string nameSpace, object obj, List> x) + { + if((obj is JsonNumber) || + (obj is JsonNull) || + (obj is string) || + (obj is JsonBoolean) || + (obj is JsonString)) + x.Add(new KeyValuePair(nameSpace, obj.ToString())); + else if(obj is JsonArray) + { + foreach(JsonObject i in (JsonArray)obj) + Explore(nameSpace, i, x); + } + else if(obj is JsonObject) + Explore(nameSpace, (JsonObject)obj, x); + else if(obj == null) + x.Add(new KeyValuePair(nameSpace, "null")); + else throw new Exception(); + } + } +} diff --git a/ProxyCore/Utility/ServerConfig.cs b/ProxyCore/Utility/ServerConfig.cs new file mode 100644 index 0000000..4bd043f --- /dev/null +++ b/ProxyCore/Utility/ServerConfig.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore; + +namespace ProxyCore +{ + public class ServerConfig : ConfigFile + { + public ServerConfig() + { + Load("server"); + } + + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Listen.Address", "127.0.0.1", "This is the address that the proxy program will be listening on. You will have to connect to this address with your MUD client. Enter 127.0.0.1 to listen only on current computer, enter LAN IP to listen only in your home network or enter 0.0.0.0 to listen on all addresses (even remote)."); + CreateSetting("Listen.Port", 4000, "This is the port that the proxy program will be listening on. You will have to enter this port in your MUD client. If you want remote connections you may have to open this port (TCP) in your firewall."); + CreateSetting("MUD.Address", "aardmud.org", "This is the address for MUD connection."); + CreateSetting("MUD.Port", 4000, "This is the port for MUD connection."); + CreateSetting("Passwords", "", "Passwords for the proxy and their user levels. For example: \"abc->1,def->2,ghi321->1\". If user enters def as password they will get user level 2. Enter as many passwords as you like. If you leave this empty, the proxy will not ask for a password and user will be authed with level 1. The password values should be between 1 and 64."); + CreateSetting("GMCP.Supports", "Core=1, Char=1, Room=1, Comm=1", "What GMCP options do we have on by default? These are modules only needed for the proxy. If your client needs another module it will enable it itself - no need to change here."); + CreateSetting("AutoConnect", 0, "Proxy program will always automatically (re)connect to MUD if there is a client online in the proxy. If there is no client online the proxy will not automatically connect. Enabling this option will force the proxy to always auto (re)connect."); + CreateSetting("ClientCompression", 1, "Enable compression for connected clients (MUD compression will always be on, even if you disable this option). Don't disable unless you are having problems with compression and can't disable client side."); + } + } + + public class CoreConfig : ConfigFile + { + public CoreConfig() + { + Load("core"); + } + + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("Updates.Core", 1, "Automatically check updates for core."); + CreateSetting("Updates.Plugins", 1, "Automatically check updates for plugins."); + } + } +} diff --git a/ProxyCore/Utility/Wrap.cs b/ProxyCore/Utility/Wrap.cs new file mode 100644 index 0000000..b73ed1a --- /dev/null +++ b/ProxyCore/Utility/Wrap.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ProxyCore +{ + public partial class Utility + { + public static string[] WrapColored(string text, int maxLength, int IndentSize) + { + if(maxLength <= 2) + return null; + + if(text.Length <= maxLength) + return new string[1] { text }; + + StringBuilder wrapBuilder = new StringBuilder(); + int count = 0; + bool printed = false; + int lastOk = 0; + int realLength = 0; + int fakeIndex = 0; + text = text.TrimEnd(); + bool didAt = false; + bool doNow = false; + for(int i = 0; i < text.Length; i++) + { + switch(text[i]) + { + case ' ': + /*case ',': + case '.': + case '?': + case '!': + case ':': + case ';': + case ')': + case ']': + case '}': + case '-':*/ + case '\t': + lastOk = i; + realLength++; + break; + + case '\n': + case '\r': + doNow = true; + lastOk = i; + realLength++; + break; + + case '@': + if(didAt) + realLength++; + didAt = !didAt; + break; + + default: + if(!didAt) + realLength++; + else + didAt = false; + break; + } + + if(realLength >= maxLength || doNow) + { + if(printed && IndentSize > 0) + wrapBuilder.Append(' ', IndentSize); + if(lastOk > 0) + { + wrapBuilder.Append(text.Substring(fakeIndex, lastOk - fakeIndex + 1).TrimEnd() + Environment.NewLine); + i = lastOk; + } + else + wrapBuilder.Append(text.Substring(fakeIndex, i - fakeIndex + 1).TrimEnd() + Environment.NewLine); + + fakeIndex = lastOk > 0 ? lastOk + 1 : i + 1; + lastOk = 0; + realLength = IndentSize; + printed = true; + count++; + doNow = false; + } + } + + if(fakeIndex < text.Length - 1) + { + if(printed && IndentSize > 0) + wrapBuilder.Append(' ', IndentSize); + wrapBuilder.Append(text.Substring(fakeIndex, text.Length - fakeIndex).TrimEnd() + Environment.NewLine); + count++; + } + + List lp = wrapBuilder.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList(); + lp.RemoveAt(lp.Count - 1); + return lp.ToArray(); + } + + public static string FormatColoredString(string msg, int len) + { + StringBuilder str = new StringBuilder(); + int real = 0; + bool at = false; + for(int i = 0; i < msg.Length; i++) + { + if(msg[i] == '@') + { + if(at) + { + real++; + str.Append("@@"); + } + at = !at; + } + else if(at) + { + str.Append("@" + msg[i].ToString()); + at = false; + } + else + { + str.Append(msg[i]); + real++; + } + } + + if(real < Math.Abs(len)) + { + if(len < 0) + str.Append(' ', Math.Abs(len) - real); + else + str.Insert(0, " ", len - real); + } + + return str.ToString(); + } + } +} diff --git a/ProxyCore/Utility/desktop.ini b/ProxyCore/Utility/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/Utility/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyCore/World.cs b/ProxyCore/World.cs new file mode 100644 index 0000000..b04b185 --- /dev/null +++ b/ProxyCore/World.cs @@ -0,0 +1,647 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore.Input; +using ProxyCore.Output; +using ProxyCore.Messages; +using ProxyCore.Scripting; +using System.Net; +using System.IO; +using System.Text.RegularExpressions; + +namespace ProxyCore +{ + public class World + { + public World() + { + Instance = this; + Log.Write("Loading plugins..."); + PluginMgr.LoadAll(); + TriggerHandler.RegisterTrigger("core.login", @"^\$gmcp\.char\.vitals\.hp ", _GMCPHP); + InputHandler.RegisterCommand("commands", "", _Commands, CMDFlags.None, null, 0, ulong.MaxValue, "core", 8); + InputHandler.RegisterCommand("lastlines", @"^(\d+)$", _LineInfo, CMDFlags.None, null, 0, ulong.MaxValue, "core", 8); + InputHandler.RegisterCommand("plugins", @"^(\w+)(\s+full)?", _PluginInfo, CMDFlags.None, null, 0, ulong.MaxValue, "core", 6); + InputHandler.RegisterCommand("shutdown", "", _Shutdown, CMDFlags.None, null, 0, ulong.MaxValue, "core", 8); + } + + private bool _Shutdown(InputData i) + { + _doShutdown = true; + return true; + } + + private bool _Commands(InputData i) + { + SendMessage("@wCommands in @Wcore@w:", i.ClientMask); + int c = WriteCommands(InputHandler.Commands, "core", i.ClientMask); + SendMessage("@C" + c + " @wcommand" + (c == 1 ? "" : "s") + " found."); + + foreach(KeyValuePair p in PluginMgr.Plugins) + { + SendMessage("", i.ClientMask); + SendMessage("@wCommands in @W" + p.Key + "@w:", i.ClientMask); + c = WriteCommands(InputHandler.Commands, p.Key, i.ClientMask); + SendMessage("@C" + c + " @wcommand" + (c == 1 ? "" : "s") + " found."); + } + + SendMessage("", i.ClientMask); + SendMessage("@WThese are commands for the proxy. If you want to see MUD commands type command.", i.ClientMask); + return true; + } + + private int WriteCommands(SortedDictionary y, string plugin, uint[] clientMask) + { + if(y == null || y.Count == 0) + return 0; + + int c = 0; + foreach(KeyValuePair x in y) + { + if(x.Value.Plugin != plugin) + continue; + if((x.Value.Flags & (CMDFlags.Disabled | CMDFlags.Hidden)) != CMDFlags.None) + continue; + string cmd = ""; + InputEntry p = x.Value.Parent; + while(p != null) + { + cmd = cmd.Insert(0, p.Command + " "); + p = p.Parent; + } + SendMessage("@Y" + cmd, clientMask); + c++; + + c += WriteCommands(x.Value.Subcommands, plugin, clientMask); + } + + return c; + } + + /// + /// Game world instance. + /// + public static World Instance + { + get; + private set; + } + + /// + /// Handle text line as if we received it from Aardwolf. + /// + /// + public void _OnReceived(string Msg) + { + TriggerHandler.HandleText(Msg, this); + } + + public void _OnConnected(bool connected) + { + isWorldReady = false; + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + if(connected) + x.Value.OnConnect(); + else + x.Value.OnDisconnect(); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + } + } + + private bool _GMCPHP(TriggerData t) + { + if(!isWorldReady) + { + isWorldReady = true; + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.OnLogin(); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + } + CheckUpdatesNow = MSTime + 5000; + } + return false; + } + + private long CheckUpdatesNow = 0; + + private bool _LineInfo(InputData i) + { + int j = 10; + if(i.Arguments.Success) + { + if(!int.TryParse(i.Arguments.Groups[1].Value, out j)) + j = 10; + } + + if(j > 100) + j = 100; + else if(j < 1) + j = 1; + + SendMessage("@wDisplaying last @Y" + j + " @wline" + (j != 1 ? "s" : "") + ":", i.ClientMask); + j = lastLine.Count - j; + if(j < 0) + j = 0; + for(; j < lastLine.Count; j++) + SendMessage(lastLine[j].Replace("@", "@@"), i.ClientMask); + if(j == 10) + SendMessage("@wType '@Wlastlines @w' to see amount of lines.", i.ClientMask); + return true; + } + + internal List lastLine = new List(); + + private bool _PluginInfo(InputData i) + { + if(!i.Arguments.Success) + { + SendMessage("@wYou have the following plugins installed:", i.ClientMask); + foreach(KeyValuePair x in PluginMgr.Plugins) + SendMessage("@Y" + string.Format("{0,-20}", x.Value.Keyword.Trim()) + " @w- " + x.Value.Name + ", version " + x.Value.Version.ToString(), i.ClientMask); + if(PluginMgr.Plugins.Count == 0) + SendMessage("@RYou have no plugins installed.", i.ClientMask); + else + { + SendMessage("", i.ClientMask); + SendMessage("@C" + PluginMgr.Plugins.Count + " @wplugin" + (PluginMgr.Plugins.Count != 1 ? "s" : "") + " found."); + SendMessage("@wUse '@Wplugin @w' for more information about a plugin.", i.ClientMask); + SendMessage("@wUse '@Wplugin full@w' for all information about a plugin.", i.ClientMask); + //SendMessage("@wUse '@Wplugin write@w' to write all information about plugin to a file.", i.ClientMask); + } + } + else + { + Plugin p = PluginMgr.Plugins.ContainsKey(i.Arguments.Groups[1].Value.ToLower().Trim()) ? PluginMgr.Plugins[i.Arguments.Groups[1].Value.ToLower().Trim()] : null; + if(p == null) + { + SendMessage("@wNo such plugin (@W" + i.Arguments.Groups[1].Value.ToLower().Trim() + "@w).", i.ClientMask); + SendMessage("@wType '@Wplugins@w' for a list of installed plugins.", i.ClientMask); + return true; + } + + SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + SendMessage("@w|@R" + p.Name.Substring(0, p.Name.Length / 2).PadLeft(35, ' ') + + p.Name.Substring(p.Name.Length / 2).PadRight(35, ' ') + "@w|", i.ClientMask); + SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + SendMessage("@w| @WKeyword @w: @g" + string.Format("{0,-55}", p.Keyword) + "@w|", i.ClientMask); + SendMessage("@w| @WAuthor @w: @g" + string.Format("{0,-55}", !string.IsNullOrEmpty(p.Author) ? p.Author : "Unknown") + "@w|", i.ClientMask); + SendMessage("@w| @WVersion @w: @Y" + string.Format("{0,-55}", p.Version) + "@w|", i.ClientMask); + if(!string.IsNullOrEmpty(p.Website)) + SendMessage("@w| @WWebsite @w: @Y" + string.Format("{0,-55}", p.Website) + "@w|", i.ClientMask); + SendMessage("@w| @WClass name @w: @g" + string.Format("{0,-55}", p.ClassName) + "@w|", i.ClientMask); + if(!string.IsNullOrEmpty(p.Description)) + { + string[] desc = Utility.WrapColored(p.Description, 54, 0); + for(int j = 0; j < desc.Length; j++) + { + SendMessage( + j == 0 + ? ("@w| @WDescription @w: @C" + string.Format("{0,-55}", desc[j]) + "@w|") + : ("@w| : @C" + string.Format("{0,-55}", desc[j]) + "@w|"), i.ClientMask); + } + } + if(p.RequiredPlayerConfig.Count != 0) + { + for(int j = 0; j < p.RequiredPlayerConfig.Count; j++) + { + SendMessage( + j == 0 + ? ("@w| @WReq. config @w: " + string.Format("{0,-55}", p.RequiredPlayerConfig[j]) + + "@w|") + : ("@w| : " + string.Format("{0,-55}", p.RequiredPlayerConfig[j]) + "@w|"), + i.ClientMask); + } + } + SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask); + if(i.Arguments.Groups[2].Length > 0 || i.Arguments.Groups[3].Length > 0) + { + int j = _PluginInfoWriteCommands(0, InputHandler.Commands, p.Keyword.ToLower().Trim(), i.ClientMask); + + int k = 0; + foreach(KeyValuePair x in TriggerHandler.TriggersName) + { + if(x.Value.Plugin != p.Keyword.ToLower().Trim()) + continue; + + if(k == 0) + { + SendMessage( + "@w| @WTriggers @w: \"" + + Utility.FormatColoredString(x.Value.PatternStr.Replace("@", "@@") + "\"", -54) + "@w|", + i.ClientMask); + } + else + { + SendMessage( + "@w| : \"" + + Utility.FormatColoredString(x.Value.PatternStr.Replace("@", "@@") + "\"", -54) + "@w|", + i.ClientMask); + } + k++; + } + + if(j != 0 || k != 0) + { + SendMessage("@w+----------------------------------------------------------------------+", + i.ClientMask); + } + } + } + + return true; + } + + private int _PluginInfoWriteCommands(int j, SortedDictionary y, string plugin, uint[] clientMask) + { + foreach(KeyValuePair x in y) + { + if(x.Value.Plugin != plugin) + continue; + + string cmd = x.Key; + InputEntry parent = x.Value.Parent; + while(parent != null) + { + cmd = cmd.Insert(0, parent.Command + " "); + parent = parent.Parent; + } + + if(j == 0) + SendMessage("@w| @WCommands @w: @c" + string.Format("{0,-55}", cmd) + "@w|", clientMask); + else + SendMessage("@w| : @c" + string.Format("{0,-55}", cmd) + "@w|", clientMask); + j++; + + if(x.Value.Subcommands != null) + j = _PluginInfoWriteCommands(j, x.Value.Subcommands, plugin, clientMask); + } + + return j; + } + + private bool isWorldReady = false; + + /// + /// Handle GMCP data that we received from Aardwolf. + /// + /// GMCP data received. + public void _OnReceived(byte[] Data) + { + string Msg = Encoding.Default.GetString(Data); + string Module = Msg.Trim().ToLower(); + if(Module.Contains(' ')) + Module = Module.Substring(0, Module.IndexOf(' ')); + Message m = new Message(true); + m.Clients = null; + m.Flags |= MessageFlags.GMCP; + m.Msg = Module; + m.MsgData = Data; + _SendMessage(m); + TriggerHandler.HandleGMCP(Msg); + } + + /// + /// This is called from proxy when it shuts down. Do NOT call from a plugin. + /// + public void Shutdown() + { + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.Shutdown(); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + } + + _doShutdown = true; + } + + /// + /// Enter input as if a client entered it. Meaning we parse it. Consider using the Execute command instead. + /// + /// Input entered. + /// Which client is this from? Enter 0 to set not from a client. + /// Authlevel of client who entered command (1...64) + public void _OnSent(string Msg, uint ClientId, int AuthLevel) + { + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.OnEnteredCommandBefore(ref Msg, ClientId, AuthLevel); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + if(Msg == null) + return; + } + + InputData i = InputHandler.HandleInput(Msg, Msg, ClientId, AuthLevel, null, this); + if(i != null) + { +#if !DEBUG + try + { +#endif + if(i.Function.Func(i)) + return; +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, i.Function.Plugin); + } +#endif + Msg = i.Command; + } + + if(Msg != null) + { + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.OnEnteredCommandAfter(ref Msg, ClientId, AuthLevel); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Value.Keyword); + } +#endif + if(Msg == null) + return; + } + _SendMessage(Msg, new uint[] {0}, ClientId != 0); + } + } + + internal void _SendMessage(string Msg, uint[] Clients, bool Natural) + { + _SendMessage(Msg, Clients, Natural, MessageFlags.None); + } + + internal void _SendMessage(string Msg, uint[] Clients, bool Natural, MessageFlags Flags) + { + _SendMessage(Msg, Clients, Natural, Flags, ulong.MaxValue); + } + + internal void _SendMessage(string Msg, uint[] Clients, bool Natural, MessageFlags Flags, ulong AuthMask) + { + Message m = new Message(Natural); + m.Clients = Clients; + m.Msg = Msg; + m.Flags = Flags; + m.AuthMask = AuthMask; + _SendMessage(m); + } + + internal void _SendMessage(Message msg) + { + _MessageData.Add(msg); + } + + /// + /// Send message to specified clients. + /// + /// Message to send. + /// Clients to send it to. Enter null to send to all connected clients. + /// Enter 0 as client to send it as a command to Aardwolf (we don't parse it though, if you want + /// parsed input use the Execute command). + public void SendMessage(string Msg, uint[] Clients) + { + _SendMessage(Msg, Clients, false); + } + + /// + /// Send a message to all connected authorized clients. + /// + /// Message to send to all authorized clients. + public void SendMessage(string Msg) + { + SendMessage(Msg, null); + } + + /// + /// Send a message to all connected authorized clients. + /// + /// Message to send to all authorized clients. + /// Authorization levels required to see this message. This is a mask. + public void SendMessage(string Msg, ulong AuthMask) + { + _SendMessage(Msg, null, false, MessageFlags.None, AuthMask); + } + + /// + /// Execute a command. + /// + /// Command to execute. + /// Allow parsing it for aliases and such, or send it directly to Aardwolf? + public void Execute(string Msg, bool allowParse) + { + Execute(Msg, allowParse, 1); + } + + /// + /// Execute a command. + /// + /// Command to execute. + /// Allow parsing it for aliases and such, or send it directly to Aardwolf? + /// Auth level that executes this command. (1...64) + public void Execute(string Msg, bool allowParse, int AuthLevel) + { + if(Msg == null) + return; + + if(allowParse) + _OnSent(Msg, uint.MaxValue, Math.Max(1, Math.Min(64, AuthLevel))); + else + _SendMessage(Msg, new uint[] { 0 }, false); + } + + /// + /// Send raw bytes to MUD. + /// + /// Bytes to send. + public void Send(byte[] Data) + { + if(Data == null) + return; + + Message m = new Message(false); + m.Clients = new uint[] { 0 }; + m.MsgData = Data; + _SendMessage(m); + } + + /// + /// Internal command for updating the world. DO NOT CALL FROM A PLUGIN! + /// + /// New mstime. + public bool Update(long msTime) + { + MSTime = msTime; + foreach(KeyValuePair x in PluginMgr.Plugins) + { +#if !DEBUG + try + { +#endif + x.Value.Update(msTime); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, x.Key); + } +#endif + } + + if(CheckUpdatesNow != 0 && msTime > CheckUpdatesNow) + { + CheckUpdatesNow = 0; + CheckUpdates(Config.GetInt32("Updates.Core", 1) != 0, Config.GetInt32("Updates.Plugins", 1) != 0, false); + } + + return _doShutdown; + } + + private bool _doShutdown = false; + + /// + /// This is messages for networking to handle. Don't touch unless you know what you are doing. + /// + public readonly List _MessageData = new List(256); + + internal CoreConfig Config = new CoreConfig(); + + /// + /// Version of Proxy. + /// + public const int Version = 7; + public const string CoreUrl = "www.duckbat.com/plugins/update.core.txt"; + public const string CoreUrl2 = "code.google.com/p/proxymud/"; + + private int GetVersion(string Url) + { + if(!_urlRegex.Match(Url).Success) + Url = "http://" + Url; + WebClient w = new WebClient(); + byte[] d = w.DownloadData(Url); + string s = Encoding.Default.GetString(d); + s = s.Replace("\r", ""); + s = s.Replace(" ", ""); + s = s.Replace("\n", ""); + return int.Parse(s); + } + + private static Regex _urlRegex = new Regex(@"^\w+://", RegexOptions.Compiled); + + /// + /// Milliseconds since program startup. + /// + public long MSTime + { + get; + private set; + } + + /// + /// Check for updates and report to all connected users if there are any. + /// + /// Check core update. + /// Check plugin updates. + /// Should we report if no updates were found? + public void CheckUpdates(bool Core, bool Plugins, bool ReportNoUpdates) + { +#if DEBUG + return; +#endif + bool s = false; + if(Core) + { + try + { + int coreVer = GetVersion(CoreUrl); + if(coreVer > Version) + { + SendMessage("@GUPDATE: @wThere is a newer version of @Wcore @wavailable. (@W" + coreVer + "@w)"); + SendMessage(" @wGo to @W" + CoreUrl2 + " @wto see more."); + s = true; + } + } + catch + { + } + } + + if(Plugins) + { + foreach(KeyValuePair x in PluginMgr.Plugins) + { + if(string.IsNullOrEmpty(x.Value.Website) || string.IsNullOrEmpty(x.Value.UpdateUrl)) + continue; + + try + { + int ver = GetVersion(x.Value.UpdateUrl); + if(ver > x.Value.Version) + { + if(s) + SendMessage(""); + SendMessage("@GUPDATE: @wThere is a newer version of @W" + x.Value.Keyword.ToLower().Trim() + " @wavailable. (@W" + ver + "@w)"); + SendMessage(" @wGo to @W" + x.Value.Website + " @wto see more."); + s = true; + } + } + catch + { + continue; + } + } + } + + if(ReportNoUpdates && !s) + SendMessage("@GUPDATE: @wNo updates found."); + } + } +} diff --git a/ProxyCore/desktop.ini b/ProxyCore/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyCore/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud.exe b/ProxyMud.exe new file mode 100644 index 0000000000000000000000000000000000000000..ea36ec7453ed6c5ebda83af31e311a29f441d763 GIT binary patch literal 22016 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

5UE@P_)=7&lwQ?tH=UA`h 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 DimyW^ literal 0 HcmV?d00001 diff --git a/ProxyMud.sln b/ProxyMud.sln new file mode 100644 index 0000000..153d3fb --- /dev/null +++ b/ProxyMud.sln @@ -0,0 +1,124 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26403.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProxyMud", "ProxyMud\ProxyMud.csproj", "{B5F8F4E3-630A-420D-8461-420C36BB865C}" + ProjectSection(ProjectDependencies) = postProject + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} = {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProxyCore", "ProxyCore\ProxyCore.csproj", "{7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandEcho", "CommandEcho\CommandEcho.csproj", "{A51883B5-8A7B-414F-92A9-A171A9846411}" + ProjectSection(ProjectDependencies) = postProject + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} = {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPHelper", "CPHelper\CPHelper.csproj", "{11D11063-A123-4F02-9C20-FB76C49A4B52}" + ProjectSection(ProjectDependencies) = postProject + {8A7FE055-E1B4-4967-9C76-CA46BA854E73} = {8A7FE055-E1B4-4967-9C76-CA46BA854E73} + {9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D} = {9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GMCPEcho", "GMCPEcho\GMCPEcho.csproj", "{86B2AA8D-5B03-4128-9C77-9E94F71CFFEE}" + ProjectSection(ProjectDependencies) = postProject + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} = {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mapper", "Mapper\Mapper.csproj", "{9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D}" + ProjectSection(ProjectDependencies) = postProject + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} = {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + {B5F8F4E3-630A-420D-8461-420C36BB865C} = {B5F8F4E3-630A-420D-8461-420C36BB865C} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MobDB", "MobDB\MobDB.csproj", "{8A7FE055-E1B4-4967-9C76-CA46BA854E73}" + ProjectSection(ProjectDependencies) = postProject + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} = {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + {9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D} = {9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GQPredict", "GQPredict\GQPredict.csproj", "{BE332B80-9B00-4B31-A3D9-15E85B2BEA83}" + ProjectSection(ProjectDependencies) = postProject + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} = {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MudLog", "MudLog\MudLog.csproj", "{2268C185-E9CC-409D-BE6F-A50ED4B900D7}" + ProjectSection(ProjectDependencies) = postProject + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} = {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonScript", "MoonScript\MoonScript.csproj", "{884C4C56-29D1-4761-B3B8-3DC2BF19D54C}" + ProjectSection(ProjectDependencies) = postProject + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} = {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySQL", "MySQL\MySQL.csproj", "{A0D29A29-FE11-4CB2-B256-B6EDD3B03A85}" + ProjectSection(ProjectDependencies) = postProject + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} = {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StayAlive", "StayAlive\StayAlive.csproj", "{CBD347BC-6BF8-417B-92B1-0BD44532A971}" + ProjectSection(ProjectDependencies) = postProject + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} = {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B5F8F4E3-630A-420D-8461-420C36BB865C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5F8F4E3-630A-420D-8461-420C36BB865C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5F8F4E3-630A-420D-8461-420C36BB865C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5F8F4E3-630A-420D-8461-420C36BB865C}.Release|Any CPU.Build.0 = Release|Any CPU + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7863FE6F-E27F-4DBA-B2CC-EDEFE4AE8382}.Release|Any CPU.Build.0 = Release|Any CPU + {A51883B5-8A7B-414F-92A9-A171A9846411}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A51883B5-8A7B-414F-92A9-A171A9846411}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A51883B5-8A7B-414F-92A9-A171A9846411}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A51883B5-8A7B-414F-92A9-A171A9846411}.Release|Any CPU.Build.0 = Release|Any CPU + {11D11063-A123-4F02-9C20-FB76C49A4B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11D11063-A123-4F02-9C20-FB76C49A4B52}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11D11063-A123-4F02-9C20-FB76C49A4B52}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11D11063-A123-4F02-9C20-FB76C49A4B52}.Release|Any CPU.Build.0 = Release|Any CPU + {86B2AA8D-5B03-4128-9C77-9E94F71CFFEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86B2AA8D-5B03-4128-9C77-9E94F71CFFEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86B2AA8D-5B03-4128-9C77-9E94F71CFFEE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86B2AA8D-5B03-4128-9C77-9E94F71CFFEE}.Release|Any CPU.Build.0 = Release|Any CPU + {9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D34EF7A-07C1-43C5-9DAA-B1DE9F8EAA7D}.Release|Any CPU.Build.0 = Release|Any CPU + {8A7FE055-E1B4-4967-9C76-CA46BA854E73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A7FE055-E1B4-4967-9C76-CA46BA854E73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A7FE055-E1B4-4967-9C76-CA46BA854E73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A7FE055-E1B4-4967-9C76-CA46BA854E73}.Release|Any CPU.Build.0 = Release|Any CPU + {BE332B80-9B00-4B31-A3D9-15E85B2BEA83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE332B80-9B00-4B31-A3D9-15E85B2BEA83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE332B80-9B00-4B31-A3D9-15E85B2BEA83}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE332B80-9B00-4B31-A3D9-15E85B2BEA83}.Release|Any CPU.Build.0 = Release|Any CPU + {2268C185-E9CC-409D-BE6F-A50ED4B900D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2268C185-E9CC-409D-BE6F-A50ED4B900D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2268C185-E9CC-409D-BE6F-A50ED4B900D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2268C185-E9CC-409D-BE6F-A50ED4B900D7}.Release|Any CPU.Build.0 = Release|Any CPU + {884C4C56-29D1-4761-B3B8-3DC2BF19D54C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {884C4C56-29D1-4761-B3B8-3DC2BF19D54C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {884C4C56-29D1-4761-B3B8-3DC2BF19D54C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {884C4C56-29D1-4761-B3B8-3DC2BF19D54C}.Release|Any CPU.Build.0 = Release|Any CPU + {A0D29A29-FE11-4CB2-B256-B6EDD3B03A85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0D29A29-FE11-4CB2-B256-B6EDD3B03A85}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0D29A29-FE11-4CB2-B256-B6EDD3B03A85}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0D29A29-FE11-4CB2-B256-B6EDD3B03A85}.Release|Any CPU.Build.0 = Release|Any CPU + {CBD347BC-6BF8-417B-92B1-0BD44532A971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBD347BC-6BF8-417B-92B1-0BD44532A971}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBD347BC-6BF8-417B-92B1-0BD44532A971}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBD347BC-6BF8-417B-92B1-0BD44532A971}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ProxyMud/.svn/all-wcprops b/ProxyMud/.svn/all-wcprops new file mode 100644 index 0000000..f53ff3f --- /dev/null +++ b/ProxyMud/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 31 +/svn/!svn/ver/57/trunk/ProxyMud +END +ProxyMud.csproj +K 25 +svn:wc:ra_dav:version-url +V 46 +/svn/!svn/ver/2/trunk/ProxyMud/ProxyMud.csproj +END +Program.cs +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/54/trunk/ProxyMud/Program.cs +END diff --git a/ProxyMud/.svn/desktop.ini b/ProxyMud/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/.svn/dir-prop-base b/ProxyMud/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/ProxyMud/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/ProxyMud/.svn/entries b/ProxyMud/.svn/entries new file mode 100644 index 0000000..70faeb2 --- /dev/null +++ b/ProxyMud/.svn/entries @@ -0,0 +1,102 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/ProxyMud +http://proxymud.googlecode.com/svn + + + +2012-02-08T08:41:07.654540Z +57 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +ProxyMud.csproj +file + + + + +2016-03-25T22:18:43.164138Z +d7be9a55f4f5a3486ffb5a351d3e4883 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +3182 + +Network +dir + +Program.cs +file + + + + +2016-03-25T22:18:43.164138Z +3df4edbd957132d9c4c5d36b317d00b6 +2012-02-02T08:38:33.263906Z +54 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +4419 + +Properties +dir + diff --git a/ProxyMud/.svn/prop-base/desktop.ini b/ProxyMud/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/.svn/props/desktop.ini b/ProxyMud/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/.svn/text-base/Program.cs.svn-base b/ProxyMud/.svn/text-base/Program.cs.svn-base new file mode 100644 index 0000000..801d342 --- /dev/null +++ b/ProxyMud/.svn/text-base/Program.cs.svn-base @@ -0,0 +1,151 @@ +//#define MONO +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; +using System.Threading; +using System.Globalization; +using System.Diagnostics; +using ProxyCore; + +namespace ProxyMud +{ + class Program + { + internal static ServerConfig Config; + + static void Main(string[] args) + { +#if !MONO + _handler += new EventHandler(Handler); + SetConsoleCtrlHandler(_handler, true); +#endif + + // Set US culture so config and other formats are universal + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US"); + Config = new ServerConfig(); + + Stopwatch watch = new Stopwatch(); + watch.Start(); + + // This is the main loop of the server program + while(canContinue) + { + // The main loop time + const uint sleepTime = 5; + + long time; + + // This is the main loop of the game world + while(canContinue) + { + // Get time before loop + time = watch.ElapsedMilliseconds; + + // Start server if needed + if(Server == null) + { +#if !DEBUG + try + { +#endif + Server = new Network.NetworkServer(); + Server.Start(); +#if !DEBUG + } + catch(Exception e) + { + Log.Error("Failed creating server: " + e.Message); + Shutdown(); + break; + } +#endif + } + + // Update server and everything +#if !DEBUG + try + { +#endif + if(Server.Update(watch.ElapsedMilliseconds)) + Shutdown(); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, "server"); + Shutdown(); + break; + } +#endif + + // Now check time after loop and see how long we should sleep to fill loop time + time = watch.ElapsedMilliseconds - time; + + // Need to sleep some time until next update + if(time <= sleepTime) + { + time = sleepTime - time; + Thread.Sleep((int)time); + } + else + Thread.Sleep(0); // Sleep at least 0 every time so program wouldn't hog CPU + } + + // Loop ended save and shut down + if(Server != null) + Server.Stop(); + } + + isFinished = true; + } + + private static Network.NetworkServer Server = null; + +#region CloseEvent +#if !MONO + [DllImport("Kernel32")] + private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add); + + private delegate bool EventHandler(CtrlType sig); + static EventHandler _handler; + + enum CtrlType + { + CTRL_C_EVENT = 0, + CTRL_BREAK_EVENT = 1, + CTRL_CLOSE_EVENT = 2, + CTRL_LOGOFF_EVENT = 5, + CTRL_SHUTDOWN_EVENT = 6 + } + + private static bool Handler(CtrlType sig) + { + switch(sig) + { + case CtrlType.CTRL_C_EVENT: + case CtrlType.CTRL_LOGOFF_EVENT: + case CtrlType.CTRL_SHUTDOWN_EVENT: + case CtrlType.CTRL_CLOSE_EVENT: + Shutdown(); + break; + default: + break; + } + while(!isFinished) + Thread.Sleep(10); + return false; + } +#endif + private static bool canContinue = true; + private static bool isFinished = false; + + private static void Shutdown() + { + canContinue = false; + } +#endregion + } +} diff --git a/ProxyMud/.svn/text-base/ProxyMud.csproj.svn-base b/ProxyMud/.svn/text-base/ProxyMud.csproj.svn-base new file mode 100644 index 0000000..9492b6a --- /dev/null +++ b/ProxyMud/.svn/text-base/ProxyMud.csproj.svn-base @@ -0,0 +1,72 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {B5F8F4E3-630A-420D-8461-420C36BB865C} + Exe + Properties + ProxyMud + ProxyMud + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + False + ..\Resources\zlib.dll + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ProxyMud/.svn/text-base/desktop.ini b/ProxyMud/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/.svn/tmp/desktop.ini b/ProxyMud/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/.svn/tmp/prop-base/desktop.ini b/ProxyMud/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/.svn/tmp/props/desktop.ini b/ProxyMud/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/.svn/tmp/text-base/desktop.ini b/ProxyMud/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Network/.svn/all-wcprops b/ProxyMud/Network/.svn/all-wcprops new file mode 100644 index 0000000..cedebc0 --- /dev/null +++ b/ProxyMud/Network/.svn/all-wcprops @@ -0,0 +1,35 @@ +K 25 +svn:wc:ra_dav:version-url +V 39 +/svn/!svn/ver/57/trunk/ProxyMud/Network +END +NetworkServer.cs +K 25 +svn:wc:ra_dav:version-url +V 56 +/svn/!svn/ver/54/trunk/ProxyMud/Network/NetworkServer.cs +END +TelnetPacket.cs +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/!svn/ver/2/trunk/ProxyMud/Network/TelnetPacket.cs +END +NetworkBase.cs +K 25 +svn:wc:ra_dav:version-url +V 53 +/svn/!svn/ver/2/trunk/ProxyMud/Network/NetworkBase.cs +END +NetworkClient.cs +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/!svn/ver/2/trunk/ProxyMud/Network/NetworkClient.cs +END +NetworkAardwolf.cs +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/!svn/ver/57/trunk/ProxyMud/Network/NetworkAardwolf.cs +END diff --git a/ProxyMud/Network/.svn/desktop.ini b/ProxyMud/Network/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Network/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Network/.svn/dir-prop-base b/ProxyMud/Network/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/ProxyMud/Network/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/ProxyMud/Network/.svn/entries b/ProxyMud/Network/.svn/entries new file mode 100644 index 0000000..ba60197 --- /dev/null +++ b/ProxyMud/Network/.svn/entries @@ -0,0 +1,198 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/ProxyMud/Network +http://proxymud.googlecode.com/svn + + + +2012-02-08T08:41:07.654540Z +57 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +NetworkServer.cs +file + + + + +2016-03-25T22:18:43.160138Z +10756e3ae397eea8fabb4ef2ff990c40 +2012-02-02T08:38:33.263906Z +54 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +9610 + +TelnetPacket.cs +file + + + + +2016-03-25T22:18:43.160138Z +56a9b9577cc5aa54970eec03cc21ab5d +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1252 + +NetworkBase.cs +file + + + + +2016-03-25T22:18:43.160138Z +8dffc14d23ee8e7db1ecaf5a9d0f7eb7 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +12319 + +NetworkClient.cs +file + + + + +2016-03-25T22:18:43.160138Z +37d0be0ab4975e29880953e6a5ea9315 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +9233 + +NetworkAardwolf.cs +file + + + + +2016-03-25T22:18:43.160138Z +0e90b02a2bd8fe59a7e49845e86f9c28 +2012-02-08T08:41:07.654540Z +57 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +6389 + diff --git a/ProxyMud/Network/.svn/prop-base/desktop.ini b/ProxyMud/Network/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Network/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Network/.svn/props/desktop.ini b/ProxyMud/Network/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Network/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Network/.svn/text-base/NetworkAardwolf.cs.svn-base b/ProxyMud/Network/.svn/text-base/NetworkAardwolf.cs.svn-base new file mode 100644 index 0000000..ac11ccc --- /dev/null +++ b/ProxyMud/Network/.svn/text-base/NetworkAardwolf.cs.svn-base @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net.Sockets; +using ComponentAce.Compression.Libs.zlib; +using ProxyCore; +using System.Text.RegularExpressions; + +namespace ProxyMud.Network +{ + internal class NetworkAardwolf : NetworkBase + { + internal NetworkAardwolf(Socket socket, NetworkServer server) + : base(socket, server, 131072) + { + + } + + #region Variables + protected string LineBuffer = string.Empty; + protected long LineBufferTimeout = 0; + #endregion + + #region Packet + protected override void HandlePacket() + { + if(inIndex >= inMaxIndex) + { + if(inStream.Length > 0) + { + OnReceived(Encoding.Default.GetString(inStream.ToArray()), inMaxIndex >= 1024); + inStream.SetLength(0); + } + return; + } + + if(zStream != null) + { + Decompress(zlibConst.Z_FULL_FLUSH); + HandlePacket(zStream_Out, 0, zStream_Length); + HandlePacket(); + return; + } + + inIndex = HandlePacket(inData, inIndex, inMaxIndex); + HandlePacket(); + } + + protected override void WriteInStream(byte[] Buf, int Index, int MaxIndex) + { + inStream.Write(Buf, Index, MaxIndex - Index); + } + #endregion + + private StringBuilder strLine = new StringBuilder(8192); + + protected override void OnReceived(string Msg, bool bigPacket) + { + if(LineBuffer.Length != 0) + { + Msg = LineBuffer + Msg; + LineBuffer = string.Empty; + } + + while(Msg.Length > 0) + { + strLine.Remove(0, strLine.Length); + int k = 0; + int i = 0; + for(; i < Msg.Length; i++) + { + if(Msg[i] == '\r') + k |= 1; + else if(Msg[i] == '\n') + k |= 2; + else + strLine.Append(Msg[i]); + + if((k & 3) == 3) + { + i++; + break; + } + } + + Msg = Msg.Substring(i); + if((k & 3) != 3) + { + LineBuffer = strLine.ToString(); + LineBufferTimeout = LastMSTime + (!bigPacket ? -1 : 500); + } + else + { + Server.World._OnReceived(Colors.FixColors(strLine.ToString(), true, true)); + } + } + } + + internal override void Update(long msTime) + { + base.Update(msTime); + + if(LineBuffer.Length != 0 && LineBufferTimeout < msTime) + { + string Msg = LineBuffer; + LineBuffer = string.Empty; + OnReceived(Msg + "\n\r", false); + } + } + + protected override void HandlePacket(TelnetPacket pkt) + { + base.HandlePacket(pkt); + + if(pkt.Type == TelnetOpcodes.GMCP && pkt.Header == TelnetOpcodes.SB && pkt.Data.Length > 0) + { + if(inStream.Length != 0) + { + OnReceived(Encoding.Default.GetString(inStream.ToArray()), false); + inStream.SetLength(0); + } + Server.World._OnReceived(pkt.Data.ToArray()); + return; + } + + if(pkt.Type == TelnetOpcodes.GMCP && pkt.Header == TelnetOpcodes.WILL) + { + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.DO, (byte)TelnetOpcodes.GMCP }); + SendGMCP(Encoding.Default.GetBytes("Core.Hello { \"client\": \"ProxyMud\", \"version\": \"" + World.Version + "\" }")); + { + if(Server.GMCPModules.Count > 0) + { + StringBuilder strModule = new StringBuilder(); + foreach(KeyValuePair x in Server.GMCPModules) + { + if(strModule.Length > 0) + strModule.Append(", "); + strModule.Append("\"" + x.Key + " " + x.Value.ToString() + "\""); + } + SendGMCP(Encoding.Default.GetBytes("Core.Supports.Set [ " + strModule.ToString() + " ]")); + } + } + return; + } + + if(pkt.Type == TelnetOpcodes.TTYPE) + { + NetworkClient c = null; + foreach(NetworkClient x in Server.Clients) + { + if(x.AuthLevel >= 1 && (c == null || x.AuthLevel > c.AuthLevel)) + c = x; + } + if(c != null) + c.Send(pkt); + return; + } + + if(pkt.Type == TelnetOpcodes.MCCP_V2) + { + if(pkt.Header == TelnetOpcodes.WILL) + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.DO, (byte)TelnetOpcodes.MCCP_V2 }); + return; + } + + if(pkt.Type == TelnetOpcodes.MCCP_V1) + { + if(pkt.Header == TelnetOpcodes.WILL) + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.DONT, (byte)TelnetOpcodes.MCCP_V1 }); + return; + } + + foreach(NetworkClient x in Server.Clients) + { + if(x.AuthLevel >= 1) + x.Send(pkt); + } + } + + internal void SendGMCP(byte[] Data) + { + byte[] b = new[] { (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SB, + (byte)TelnetOpcodes.GMCP }; + byte[] c = new[] { (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SE }; + Send(b.Concat(Data).Concat(c).ToArray()); + } + } +} diff --git a/ProxyMud/Network/.svn/text-base/NetworkBase.cs.svn-base b/ProxyMud/Network/.svn/text-base/NetworkBase.cs.svn-base new file mode 100644 index 0000000..575014e --- /dev/null +++ b/ProxyMud/Network/.svn/text-base/NetworkBase.cs.svn-base @@ -0,0 +1,344 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net.Sockets; +using System.IO; +using ComponentAce.Compression.Libs.zlib; +using ProxyCore; + +namespace ProxyMud.Network +{ + internal class NetworkBase + { + protected NetworkBase(Socket socket, NetworkServer server, int inBufferSize) + { + Server = server; + Socket = socket; + inStream = new MemoryStream(inBufferSize); + Socket.Blocking = false; + } + + #region Variables + protected readonly NetworkServer Server; + protected Socket Socket; + protected MemoryStream inStream; + protected TelnetPacket telnetPacket; + protected ZStream zStream; + protected static int zStream_Length; + protected static byte[] zStream_Out = new byte[65536]; + protected static int inIndex; + protected static int inMaxIndex; + protected static byte[] inData = new byte[65536]; + #endregion + + #region Networking + internal bool Receive() + { + if(Socket == null) + return false; + + SocketError err; + inMaxIndex = Socket.Receive(inData, 0, inData.Length, SocketFlags.None, out err); + if(err != SocketError.WouldBlock && inMaxIndex == 0) + { + Socket.Close(); + Socket = null; + return false; + } + + if(inMaxIndex == 0) + return true; + + inIndex = 0; + HandlePacket(); + return true; + } + + internal void Send(byte[] Data) + { + if(Socket == null) + return; + + if(zStream != null && this is NetworkClient) + { + Compress(Data, zlibConst.Z_FULL_FLUSH); + if(zStream_Length > 0) + Socket.Send(zStream_Out, zStream_Length, SocketFlags.None); + } + else + Socket.Send(Data, SocketFlags.None); + } + + internal void Send(TelnetPacket pkt) + { + if(Socket == null) + return; + + if(pkt.Header == TelnetOpcodes.SB && pkt.Data.Length != 0) + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.SB, (byte)pkt.Type }.Concat( + pkt.Data.ToArray()).Concat(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.SE }).ToArray()); + else + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)pkt.Header, (byte)pkt.Type }); + } + + internal void Disconnect() + { + if(Socket == null) + return; + + Socket.Close(); + Socket = null; + } + #endregion + + #region Compression + protected bool StartCompression(TelnetOpcodes type) + { + if(this is NetworkAardwolf) + throw new Exception("Trying to start compression on Aardwolf connection!"); + + if(zStream != null) + return false; + + if(type == TelnetOpcodes.MCCP_V1) + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.SB, (byte)TelnetOpcodes.MCCP_V1, + (byte)TelnetOpcodes.WILL, (byte)TelnetOpcodes.SE }); + else + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.SB, (byte)TelnetOpcodes.MCCP_V2, + (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.SE }); + + zStream = new ZStream(); + zStream.deflateInit(6); + return true; + } + + protected void EndCompression() + { + if(this is NetworkAardwolf) + throw new Exception("Trying to end compression on Aardwolf connection!"); + + if(zStream == null) + return; + + byte[] d = new byte[0]; + Compress(d, zlibConst.Z_FINISH); + if(zStream_Length > 0) + Socket.Send(zStream_Out, zStream_Length, SocketFlags.None); + + zStream.deflateEnd(); + zStream.free(); + zStream = null; + } + + private void StartDecompression() + { + if(this is NetworkClient) + throw new Exception("Trying to start decompression on Client connection!"); + + if(zStream != null) + return; + + zStream = new ZStream(); + zStream.inflateInit(); + } + + internal void Compress(byte[] Data, int type) + { + if(this is NetworkAardwolf) + throw new Exception("Trying to compress data on Aardwolf connection!"); + + zStream_Length = 0; + + zStream.avail_in = Data.Length; + zStream.next_in = Data; + zStream.next_in_index = 0; + + zStream.next_out = zStream_Out; + zStream.next_out_index = 0; + zStream.avail_out = zStream_Out.Length; + + switch(zStream.deflate(type)) + { + case zlibConst.Z_OK: + //inIndex = zStream.next_in_index; + zStream_Length = (int)zStream.total_out; + zStream.total_out = 0; + zStream.next_in = null; + break; + + case zlibConst.Z_STREAM_END: + //inIndex = zStream.next_in_index; + zStream_Length = (int)zStream.total_out; + zStream.deflateEnd(); + zStream.free(); + zStream = null; + break; + + default: + //case zlibConst.Z_STREAM_ERROR: + throw new Exception("Error while compressing: " + (zStream.msg ?? "unknown error") + "!"); + } + } + + internal void Decompress(int type) + { + if(this is NetworkClient) + throw new Exception("Trying to decompress data on Client connection!"); + + zStream_Length = 0; + + zStream.avail_in = inMaxIndex - inIndex; + zStream.next_in = inData; + zStream.next_in_index = inIndex; + + zStream.next_out = zStream_Out; + zStream.next_out_index = 0; + zStream.avail_out = zStream_Out.Length; + + switch(zStream.inflate(type)) + { + case zlibConst.Z_OK: + inIndex = zStream.next_in_index; + zStream_Length = (int)zStream.total_out; + zStream.total_out = 0; + break; + + case zlibConst.Z_STREAM_END: + inIndex = zStream.next_in_index; + zStream_Length = (int)zStream.total_out; + zStream.inflateEnd(); + zStream.free(); + zStream = null; + break; + + default: + //case zlibConst.Z_STREAM_ERROR: + throw new Exception("Error while decompressing: " + (zStream.msg ?? "unknown error") + "!"); + } + } + #endregion + + #region Packet + protected virtual void HandlePacket() + { + + } + + protected virtual void HandlePacket(TelnetPacket pkt) + { + + } + + protected int HandlePacket(byte[] Buf, int Index, int MaxIndex) + { + if(Index >= MaxIndex) + return MaxIndex; + + if(telnetPacket != null) + { + switch(telnetPacket.State) + { + case TelnetStates.None: + telnetPacket.Header = (TelnetOpcodes)Buf[Index]; + telnetPacket.State = TelnetStates.Header; + return HandlePacket(Buf, Index + 1, MaxIndex); + + case TelnetStates.Header: + telnetPacket.Type = (TelnetOpcodes)Buf[Index]; + telnetPacket.State = telnetPacket.Header == TelnetOpcodes.SB ? TelnetStates.Data : TelnetStates.End; + if(telnetPacket.State == TelnetStates.End) + { + HandlePacket(telnetPacket); + telnetPacket = null; + } + else + telnetPacket.Data = new MemoryStream(512); + return HandlePacket(Buf, Index + 1, MaxIndex); + + case TelnetStates.Data: + { + for(int i = Index; i < MaxIndex; i++) + { + if(telnetPacket.HadIAC && Buf[i] == (byte)TelnetOpcodes.SE) + { + telnetPacket.State = TelnetStates.End; + HandlePacket(telnetPacket); + if(zStream == null && (telnetPacket.Type == TelnetOpcodes.MCCP_V1 || telnetPacket.Type == TelnetOpcodes.MCCP_V2)) + { + telnetPacket = null; + StartDecompression(); + return i + 1; + } + telnetPacket = null; + return HandlePacket(Buf, i + 1, MaxIndex); + } + if(Buf[i] == (byte)TelnetOpcodes.IAC || (Buf[i] == (byte)TelnetOpcodes.WILL && telnetPacket.Type == TelnetOpcodes.MCCP_V1)) + { + if(!telnetPacket.HadIAC) + { + if(i - Index > 0) + { + telnetPacket.Data.Write(Buf, Index, i - Index); + Index = i; + } + telnetPacket.HadIAC = true; + } + else + telnetPacket.Data.Write(new[] { (byte)TelnetOpcodes.IAC }, 0, 1); + } + else + { + if(telnetPacket.HadIAC) + { + telnetPacket.HadIAC = false; + telnetPacket.Data.Write(new[] { (byte)TelnetOpcodes.IAC }, 0, 1); + } + } + } + + if(Index < MaxIndex) + { + if(Buf[MaxIndex - 1] != (byte)TelnetOpcodes.IAC) + telnetPacket.Data.Write(Buf, Index, MaxIndex - Index); + } + return MaxIndex; + } + } + } + + for(int i = Index; i < MaxIndex; i++) + { + if(Buf[i] == (byte)TelnetOpcodes.IAC) + { + if(i - Index > 0) + WriteInStream(Buf, Index, i); + telnetPacket = new TelnetPacket(); + return HandlePacket(Buf, i + 1, MaxIndex); + } + } + + if(MaxIndex - Index > 0) + WriteInStream(Buf, Index, MaxIndex); + return MaxIndex; + } + + protected virtual void WriteInStream(byte[] Buf, int Index, int MaxIndex) + { + + } + + protected virtual void OnReceived(string Msg, bool bigPacket) + { + + } + #endregion + + protected long LastMSTime; + + internal virtual void Update(long msTime) + { + LastMSTime = msTime; + } + } +} diff --git a/ProxyMud/Network/.svn/text-base/NetworkClient.cs.svn-base b/ProxyMud/Network/.svn/text-base/NetworkClient.cs.svn-base new file mode 100644 index 0000000..5f882a9 --- /dev/null +++ b/ProxyMud/Network/.svn/text-base/NetworkClient.cs.svn-base @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net.Sockets; +using ComponentAce.Compression.Libs.zlib; +using System.IO; + +namespace ProxyMud.Network +{ + internal class NetworkClient : NetworkBase + { + internal NetworkClient(Socket socket, NetworkServer server) + : base(socket, server, 4096) + { + Id = ++_highId; + } + + #region Variables + private static uint _highId = 0; + internal readonly uint Id; + internal List GMCPModules = new List(); + internal int AuthLevel = -1; + private TelnetOpcodes CompressionType = TelnetOpcodes.IAC; + #endregion + + #region Packet + protected override void HandlePacket() + { + if(inIndex >= inMaxIndex) + return; + + inIndex = HandlePacket(inData, inIndex, inMaxIndex); + HandlePacket(); + } + protected override void WriteInStream(byte[] Buf, int Index, int MaxIndex) + { + for(int i = Index; i < MaxIndex; i++) + { + if(Buf[i] == 0x8) + { + if(inStream.Length > 0) + inStream.SetLength(inStream.Length - 1); + } + else if(Buf[i] == 0xA) + { + continue; + } + else if(Buf[i] == 0xD) + { + OnReceived(Encoding.Default.GetString(inStream.ToArray()), false); + inStream.SetLength(0); + } + else + inStream.WriteByte(Buf[i]); + } + } + #endregion + + protected override void OnReceived(string Msg, bool bigPacket) + { + if(AuthLevel >= 1) + { + Server.World._OnSent(Msg, Id, AuthLevel); + return; + } + + if(Server.Passwords.ContainsKey(Msg)) + { + AuthLevel = Server.Passwords[Msg]; + OnAuthed(); + } + } + + internal bool HasGMCPModule(string mod) + { + if(GMCPModules.Contains(mod)) + return true; + + string[] m = mod.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); + string n = string.Empty; + for(int i = 0; i < m.Length; i++) + { + n += (i != 0 ? "." : "") + m[i]; + if(GMCPModules.Contains(n)) + return true; + } + + return false; + } + + internal void OnAuthed() + { + Send(new[] { (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.WILL, + (byte)TelnetOpcodes.GMCP }); + Send(Encoding.Default.GetBytes("You are now connected to ProxyMud.\n\r")); + } + + protected override void HandlePacket(TelnetPacket pkt) + { + base.HandlePacket(pkt); + + if(pkt.Header == TelnetOpcodes.SB && pkt.Type == TelnetOpcodes.GMCP && pkt.Data.Length > 0) + { + string Msg = Encoding.Default.GetString(pkt.Data.ToArray()).ToLower(); + if(!Msg.StartsWith("core.supports.") && !Msg.StartsWith("core.hello")) + { + if(Server.Aardwolf != null) + Server.Aardwolf.SendGMCP(pkt.Data.ToArray()); + } + else if(Msg.StartsWith("core.supports.")) + { + try + { + Msg = Msg.Substring("core.supports.".Length); + if(Msg.StartsWith("add ")) + { + string[] v = + Msg.ToLower().Substring(Msg.IndexOf(' ') + 1).Replace("[", "").Replace("]", "").Replace( + "\"", "").Replace(" ", "").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string x in v) + { + if(!x.EndsWith("0")) + { + if(!GMCPModules.Contains(x.Substring(0, x.Length - 1))) + { + GMCPModules.Add(x.Substring(0, x.Length - 1)); + string b = x.Substring(0, x.Length - 1); + if(b.Contains('.')) + b = b.Substring(0, b.IndexOf('.')); + if(!Server.GMCPModules.ContainsKey(b) || Server.GMCPModules[b] == 0) + { + if(Server.Aardwolf != null) + Server.Aardwolf.SendGMCP(Encoding.Default.GetBytes("Core.Supports.Add [ \"" + b + " " + x[x.Length - 1].ToString() + "\" ]")); + } + } + } + else + GMCPModules.Remove(x.Substring(0, x.Length - 1)); + } + } + else if(Msg.StartsWith("set ")) + { + string[] v = + Msg.ToLower().Substring(Msg.IndexOf(' ') + 1).Replace("[", "").Replace("]", "").Replace( + "\"", "").Replace(" ", "").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string x in v) + { + if(!x.EndsWith("0")) + { + if(!GMCPModules.Contains(x.Substring(0, x.Length - 1))) + { + GMCPModules.Add(x.Substring(0, x.Length - 1)); + string b = x.Substring(0, x.Length - 1); + if(b.Contains('.')) + b = b.Substring(0, b.IndexOf('.')); + if(!Server.GMCPModules.ContainsKey(b) || Server.GMCPModules[b] == 0) + { + if(Server.Aardwolf != null) + Server.Aardwolf.SendGMCP(Encoding.Default.GetBytes("Core.Supports.Add [ \"" + b + " " + x[x.Length - 1].ToString() + "\" ]")); + } + } + } + else + GMCPModules.Remove(x.Substring(0, x.Length - 1)); + } + } + else if(Msg.StartsWith("remove ")) + { + string[] v = + Msg.ToLower().Substring(Msg.IndexOf(' ') + 1).Replace("[", "").Replace("]", "").Replace( + "\"", "").Replace(" ", "").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string x in v) + GMCPModules.Remove(x); + } + } + catch + { + } + } + } + + if(pkt.Type == TelnetOpcodes.GMCP) + return; + + if(pkt.Type == TelnetOpcodes.MCCP_V2) + { + if(pkt.Header == TelnetOpcodes.DO) + { + if(StartCompression(TelnetOpcodes.MCCP_V2)) + CompressionType = TelnetOpcodes.MCCP_V2; + } + else if(pkt.Header == TelnetOpcodes.DONT) + { + if(CompressionType == TelnetOpcodes.MCCP_V2) + { + EndCompression(); + CompressionType = TelnetOpcodes.IAC; + } + } + return; + } + + if(pkt.Type == TelnetOpcodes.MCCP_V1) + { + if(pkt.Header == TelnetOpcodes.DO) + { + if(StartCompression(TelnetOpcodes.MCCP_V1)) + CompressionType = TelnetOpcodes.MCCP_V1; + } + else if(pkt.Header == TelnetOpcodes.DONT) + { + if(CompressionType == TelnetOpcodes.MCCP_V1) + { + EndCompression(); + CompressionType = TelnetOpcodes.IAC; + } + } + return; + } + + if(Server.Aardwolf != null) + Server.Aardwolf.Send(pkt); + } + } +} diff --git a/ProxyMud/Network/.svn/text-base/NetworkServer.cs.svn-base b/ProxyMud/Network/.svn/text-base/NetworkServer.cs.svn-base new file mode 100644 index 0000000..9f99fb6 --- /dev/null +++ b/ProxyMud/Network/.svn/text-base/NetworkServer.cs.svn-base @@ -0,0 +1,257 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore; +using System.Net.Sockets; +using System.IO; +using System.Net; +using ProxyCore.Messages; +using System.Text.RegularExpressions; + +namespace ProxyMud.Network +{ + internal class NetworkServer + { + internal NetworkServer() + { + string support = Program.Config.GetString("GMCP.Supports", "Core=1, Char=1, Room=1, Comm=1"); + Match m; + while((m = _loadSupport.Match(support)).Success) + { + support = support.Substring(m.Groups[0].Length); + int i; + if(!int.TryParse(m.Groups[2].Value, out i)) + continue; + GMCPModules[m.Groups[1].Value.ToLower()] = i; + } + } + + private static readonly Regex _loadSupport = new Regex(@"^([\w\.]+)\s*=\s*(\d+),?\s*", RegexOptions.Compiled); + internal Dictionary GMCPModules = new Dictionary(); + internal World World = new World(); + internal List Clients = new List(); + internal NetworkAardwolf Aardwolf = null; + internal TcpListener Server = null; + private readonly MemoryStream strMessage = new MemoryStream(131072); + internal Dictionary Passwords = new Dictionary(); + private static Regex _loadPw = new Regex(@"(.+?)\s*->\s*(\d+),?\s*", RegexOptions.Compiled); + + internal void Start() + { + IPAddress ip = IPAddress.Parse(Program.Config.GetString("Listen.Address", "127.0.0.1")); + if(Server == null) + Server = new TcpListener(ip, Program.Config.GetInt32("Listen.Port", 4000)); + { + Passwords.Clear(); + string pw = Program.Config.GetString("Passwords", ""); + if(!string.IsNullOrEmpty(pw)) + { + Match m; + while((m = _loadPw.Match(pw)).Success) + { + pw = pw.Substring(m.Groups[0].Value.Length); + int i; + if(!int.TryParse(m.Groups[2].Value, out i)) + continue; + if(i < 1) + i = 1; + else if(i > 64) + i = 64; + Passwords[m.Groups[1].Value] = i; + } + } + } + Server.Start(); + + Log.Write("Starting core, version " + World.Version + "."); + Log.Write("Waiting for connections on " + Program.Config.GetString("Listen.Address", "127.0.0.1") + ":" + Program.Config.GetInt32("Listen.Port", 4000) + "."); + } + + internal void Stop() + { + try + { + Server.Stop(); + } + catch + { + } + + World.Shutdown(); + } + + internal void DisconnectAll() + { + if(Aardwolf != null) + { + Aardwolf.Disconnect(); + Aardwolf = null; + World._OnConnected(false); + } + + foreach(NetworkClient x in Clients) + x.Disconnect(); + Clients.Clear(); + } + + internal bool Update(long msTime) + { + if(Server != null && Server.Pending()) + { + NetworkClient c = new NetworkClient(Server.AcceptSocket(), this); + Clients.Add(c); + if(Program.Config.GetInt32("ClientCompression", 1) != 0) + { + c.Send(new[] + { + (byte) TelnetOpcodes.IAC, + (byte) TelnetOpcodes.WILL, + (byte) TelnetOpcodes.MCCP_V2, + (byte) TelnetOpcodes.IAC, + (byte) TelnetOpcodes.WILL, + (byte) TelnetOpcodes.MCCP_V1 + }); + } + if(Passwords.Count != 0) + c.Send(Encoding.Default.GetBytes("Proxy password?\n\r")); + else + { + c.AuthLevel = 1; + c.OnAuthed(); + } + } + + bool r = World.Update(msTime); + + if(Aardwolf != null) + { + if(!Aardwolf.Receive()) + { + Aardwolf = null; + World._OnConnected(false); + } + else + Aardwolf.Update(msTime); + } + + bool hadAuthedClient = false; + for(int i = Clients.Count - 1; i >= 0; i--) + { + if(!Clients[i].Receive()) + { + Clients.RemoveAt(i); + continue; + } + + if(Clients[i].AuthLevel >= 1) + hadAuthedClient = true; + } + + if(Aardwolf == null && (hadAuthedClient || Program.Config.GetInt32("AutoConnect", 0) != 0)) + { + Log.Write("Connecting to " + Program.Config.GetString("MUD.Address", "aardmud.org") + ":" + Program.Config.GetInt32("MUD.Port", 4000).ToString()); + try + { + TcpClient t = new TcpClient(Program.Config.GetString("MUD.Address", "aardmud.org"), + Program.Config.GetInt32("MUD.Port", 4000)); + Aardwolf = new NetworkAardwolf(t.Client, this); + World._OnConnected(true); + } + catch(Exception e) + { + Log.Write("Failed connection to Aardwolf: " + e.Message); + } + } + + if(Aardwolf != null) + { + if(strMessage.Length > 0) + strMessage.SetLength(0); + foreach(Message m in World._MessageData) + { + if(m.Clients == null || !m.Clients.Contains((uint)0)) + continue; + + if((m.Flags & MessageFlags.GMCP) != MessageFlags.None) + { + if(m.MsgData == null || m.MsgData.Length == 0) + continue; + + strMessage.Write(new byte[] { + (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SB, + (byte)TelnetOpcodes.GMCP + }, 0, 3); + strMessage.Write(m.MsgData, 0, m.MsgData.Length); + strMessage.Write(new byte[] { + (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SE}, 0, 2); + } + else + { + byte[] data = m.MsgData ?? Encoding.Default.GetBytes(m.Msg + m.LineEnding); + strMessage.Write(data, 0, data.Length); + } + } + + if(strMessage.Length != 0) + Aardwolf.Send(strMessage.ToArray()); + } + + for(int i = Clients.Count - 1; i >= 0; i--) + { + if(strMessage.Length > 0) + strMessage.SetLength(0); + + Clients[i].Update(msTime); + + if(Clients[i].AuthLevel < 1) + continue; + + foreach(Message m in World._MessageData) + { + if(m.Clients != null && !m.Clients.Contains(Clients[i].Id)) + continue; + + if((m.AuthMask & ((ulong)1 << (Clients[i].AuthLevel - 1))) == 0) + continue; + + if((m.Flags & MessageFlags.GMCP) != MessageFlags.None) + { + if(!Clients[i].HasGMCPModule(m.Msg.ToLower())) + continue; + + if(m.MsgData == null || m.MsgData.Length == 0) + continue; + + strMessage.Write(new[] { + (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SB, + (byte)TelnetOpcodes.GMCP + }, 0, 3); + strMessage.Write(m.MsgData, 0, m.MsgData.Length); + strMessage.Write(new[] { + (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SE}, 0, 2); + } + else + { + string msg = m.Msg; + msg = Colors.FixColors(msg, false, true); + byte[] data = Encoding.Default.GetBytes(msg + m.LineEnding); + strMessage.Write(data, 0, data.Length); + } + } + + if(strMessage.Length == 0) + continue; + + Clients[i].Send(strMessage.ToArray()); + } + + World._MessageData.Clear(); + return r; + } + } +} diff --git a/ProxyMud/Network/.svn/text-base/TelnetPacket.cs.svn-base b/ProxyMud/Network/.svn/text-base/TelnetPacket.cs.svn-base new file mode 100644 index 0000000..9920e90 --- /dev/null +++ b/ProxyMud/Network/.svn/text-base/TelnetPacket.cs.svn-base @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace ProxyMud.Network +{ + internal enum TelnetOpcodes : byte + { + SE = 240, + SB = 250, + WILL = 251, + WONT = 252, + DO = 253, + DONT = 254, + IAC = 255, + + TTYPE = 24, + MCCP_V1 = 85, + MCCP_V2 = 86, + GMCP = 201, + } + + internal class TelnetPacket + { + internal TelnetStates State; + internal TelnetOpcodes Header; + internal TelnetOpcodes Type; + internal MemoryStream Data; + internal bool HadIAC = false; + } + + internal enum TelnetStates + { + ///

+ /// Just received IAC, nothing else. + /// + None, + + /// + /// Have header (what packet does). + /// + Header, + + /// + /// Have type of packet. + /// + Type, + + /// + /// Gathering data right now waiting for IAC SE. + /// + Data, + + /// + /// Finished packet. + /// + End, + } +} diff --git a/ProxyMud/Network/.svn/text-base/desktop.ini b/ProxyMud/Network/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Network/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Network/.svn/tmp/desktop.ini b/ProxyMud/Network/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Network/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Network/.svn/tmp/prop-base/desktop.ini b/ProxyMud/Network/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Network/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Network/.svn/tmp/props/desktop.ini b/ProxyMud/Network/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Network/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Network/.svn/tmp/text-base/desktop.ini b/ProxyMud/Network/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Network/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Network/NetworkAardwolf.cs b/ProxyMud/Network/NetworkAardwolf.cs new file mode 100644 index 0000000..ac11ccc --- /dev/null +++ b/ProxyMud/Network/NetworkAardwolf.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net.Sockets; +using ComponentAce.Compression.Libs.zlib; +using ProxyCore; +using System.Text.RegularExpressions; + +namespace ProxyMud.Network +{ + internal class NetworkAardwolf : NetworkBase + { + internal NetworkAardwolf(Socket socket, NetworkServer server) + : base(socket, server, 131072) + { + + } + + #region Variables + protected string LineBuffer = string.Empty; + protected long LineBufferTimeout = 0; + #endregion + + #region Packet + protected override void HandlePacket() + { + if(inIndex >= inMaxIndex) + { + if(inStream.Length > 0) + { + OnReceived(Encoding.Default.GetString(inStream.ToArray()), inMaxIndex >= 1024); + inStream.SetLength(0); + } + return; + } + + if(zStream != null) + { + Decompress(zlibConst.Z_FULL_FLUSH); + HandlePacket(zStream_Out, 0, zStream_Length); + HandlePacket(); + return; + } + + inIndex = HandlePacket(inData, inIndex, inMaxIndex); + HandlePacket(); + } + + protected override void WriteInStream(byte[] Buf, int Index, int MaxIndex) + { + inStream.Write(Buf, Index, MaxIndex - Index); + } + #endregion + + private StringBuilder strLine = new StringBuilder(8192); + + protected override void OnReceived(string Msg, bool bigPacket) + { + if(LineBuffer.Length != 0) + { + Msg = LineBuffer + Msg; + LineBuffer = string.Empty; + } + + while(Msg.Length > 0) + { + strLine.Remove(0, strLine.Length); + int k = 0; + int i = 0; + for(; i < Msg.Length; i++) + { + if(Msg[i] == '\r') + k |= 1; + else if(Msg[i] == '\n') + k |= 2; + else + strLine.Append(Msg[i]); + + if((k & 3) == 3) + { + i++; + break; + } + } + + Msg = Msg.Substring(i); + if((k & 3) != 3) + { + LineBuffer = strLine.ToString(); + LineBufferTimeout = LastMSTime + (!bigPacket ? -1 : 500); + } + else + { + Server.World._OnReceived(Colors.FixColors(strLine.ToString(), true, true)); + } + } + } + + internal override void Update(long msTime) + { + base.Update(msTime); + + if(LineBuffer.Length != 0 && LineBufferTimeout < msTime) + { + string Msg = LineBuffer; + LineBuffer = string.Empty; + OnReceived(Msg + "\n\r", false); + } + } + + protected override void HandlePacket(TelnetPacket pkt) + { + base.HandlePacket(pkt); + + if(pkt.Type == TelnetOpcodes.GMCP && pkt.Header == TelnetOpcodes.SB && pkt.Data.Length > 0) + { + if(inStream.Length != 0) + { + OnReceived(Encoding.Default.GetString(inStream.ToArray()), false); + inStream.SetLength(0); + } + Server.World._OnReceived(pkt.Data.ToArray()); + return; + } + + if(pkt.Type == TelnetOpcodes.GMCP && pkt.Header == TelnetOpcodes.WILL) + { + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.DO, (byte)TelnetOpcodes.GMCP }); + SendGMCP(Encoding.Default.GetBytes("Core.Hello { \"client\": \"ProxyMud\", \"version\": \"" + World.Version + "\" }")); + { + if(Server.GMCPModules.Count > 0) + { + StringBuilder strModule = new StringBuilder(); + foreach(KeyValuePair x in Server.GMCPModules) + { + if(strModule.Length > 0) + strModule.Append(", "); + strModule.Append("\"" + x.Key + " " + x.Value.ToString() + "\""); + } + SendGMCP(Encoding.Default.GetBytes("Core.Supports.Set [ " + strModule.ToString() + " ]")); + } + } + return; + } + + if(pkt.Type == TelnetOpcodes.TTYPE) + { + NetworkClient c = null; + foreach(NetworkClient x in Server.Clients) + { + if(x.AuthLevel >= 1 && (c == null || x.AuthLevel > c.AuthLevel)) + c = x; + } + if(c != null) + c.Send(pkt); + return; + } + + if(pkt.Type == TelnetOpcodes.MCCP_V2) + { + if(pkt.Header == TelnetOpcodes.WILL) + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.DO, (byte)TelnetOpcodes.MCCP_V2 }); + return; + } + + if(pkt.Type == TelnetOpcodes.MCCP_V1) + { + if(pkt.Header == TelnetOpcodes.WILL) + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.DONT, (byte)TelnetOpcodes.MCCP_V1 }); + return; + } + + foreach(NetworkClient x in Server.Clients) + { + if(x.AuthLevel >= 1) + x.Send(pkt); + } + } + + internal void SendGMCP(byte[] Data) + { + byte[] b = new[] { (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SB, + (byte)TelnetOpcodes.GMCP }; + byte[] c = new[] { (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SE }; + Send(b.Concat(Data).Concat(c).ToArray()); + } + } +} diff --git a/ProxyMud/Network/NetworkBase.cs b/ProxyMud/Network/NetworkBase.cs new file mode 100644 index 0000000..575014e --- /dev/null +++ b/ProxyMud/Network/NetworkBase.cs @@ -0,0 +1,344 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net.Sockets; +using System.IO; +using ComponentAce.Compression.Libs.zlib; +using ProxyCore; + +namespace ProxyMud.Network +{ + internal class NetworkBase + { + protected NetworkBase(Socket socket, NetworkServer server, int inBufferSize) + { + Server = server; + Socket = socket; + inStream = new MemoryStream(inBufferSize); + Socket.Blocking = false; + } + + #region Variables + protected readonly NetworkServer Server; + protected Socket Socket; + protected MemoryStream inStream; + protected TelnetPacket telnetPacket; + protected ZStream zStream; + protected static int zStream_Length; + protected static byte[] zStream_Out = new byte[65536]; + protected static int inIndex; + protected static int inMaxIndex; + protected static byte[] inData = new byte[65536]; + #endregion + + #region Networking + internal bool Receive() + { + if(Socket == null) + return false; + + SocketError err; + inMaxIndex = Socket.Receive(inData, 0, inData.Length, SocketFlags.None, out err); + if(err != SocketError.WouldBlock && inMaxIndex == 0) + { + Socket.Close(); + Socket = null; + return false; + } + + if(inMaxIndex == 0) + return true; + + inIndex = 0; + HandlePacket(); + return true; + } + + internal void Send(byte[] Data) + { + if(Socket == null) + return; + + if(zStream != null && this is NetworkClient) + { + Compress(Data, zlibConst.Z_FULL_FLUSH); + if(zStream_Length > 0) + Socket.Send(zStream_Out, zStream_Length, SocketFlags.None); + } + else + Socket.Send(Data, SocketFlags.None); + } + + internal void Send(TelnetPacket pkt) + { + if(Socket == null) + return; + + if(pkt.Header == TelnetOpcodes.SB && pkt.Data.Length != 0) + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.SB, (byte)pkt.Type }.Concat( + pkt.Data.ToArray()).Concat(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.SE }).ToArray()); + else + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)pkt.Header, (byte)pkt.Type }); + } + + internal void Disconnect() + { + if(Socket == null) + return; + + Socket.Close(); + Socket = null; + } + #endregion + + #region Compression + protected bool StartCompression(TelnetOpcodes type) + { + if(this is NetworkAardwolf) + throw new Exception("Trying to start compression on Aardwolf connection!"); + + if(zStream != null) + return false; + + if(type == TelnetOpcodes.MCCP_V1) + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.SB, (byte)TelnetOpcodes.MCCP_V1, + (byte)TelnetOpcodes.WILL, (byte)TelnetOpcodes.SE }); + else + Send(new[] { (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.SB, (byte)TelnetOpcodes.MCCP_V2, + (byte)TelnetOpcodes.IAC, (byte)TelnetOpcodes.SE }); + + zStream = new ZStream(); + zStream.deflateInit(6); + return true; + } + + protected void EndCompression() + { + if(this is NetworkAardwolf) + throw new Exception("Trying to end compression on Aardwolf connection!"); + + if(zStream == null) + return; + + byte[] d = new byte[0]; + Compress(d, zlibConst.Z_FINISH); + if(zStream_Length > 0) + Socket.Send(zStream_Out, zStream_Length, SocketFlags.None); + + zStream.deflateEnd(); + zStream.free(); + zStream = null; + } + + private void StartDecompression() + { + if(this is NetworkClient) + throw new Exception("Trying to start decompression on Client connection!"); + + if(zStream != null) + return; + + zStream = new ZStream(); + zStream.inflateInit(); + } + + internal void Compress(byte[] Data, int type) + { + if(this is NetworkAardwolf) + throw new Exception("Trying to compress data on Aardwolf connection!"); + + zStream_Length = 0; + + zStream.avail_in = Data.Length; + zStream.next_in = Data; + zStream.next_in_index = 0; + + zStream.next_out = zStream_Out; + zStream.next_out_index = 0; + zStream.avail_out = zStream_Out.Length; + + switch(zStream.deflate(type)) + { + case zlibConst.Z_OK: + //inIndex = zStream.next_in_index; + zStream_Length = (int)zStream.total_out; + zStream.total_out = 0; + zStream.next_in = null; + break; + + case zlibConst.Z_STREAM_END: + //inIndex = zStream.next_in_index; + zStream_Length = (int)zStream.total_out; + zStream.deflateEnd(); + zStream.free(); + zStream = null; + break; + + default: + //case zlibConst.Z_STREAM_ERROR: + throw new Exception("Error while compressing: " + (zStream.msg ?? "unknown error") + "!"); + } + } + + internal void Decompress(int type) + { + if(this is NetworkClient) + throw new Exception("Trying to decompress data on Client connection!"); + + zStream_Length = 0; + + zStream.avail_in = inMaxIndex - inIndex; + zStream.next_in = inData; + zStream.next_in_index = inIndex; + + zStream.next_out = zStream_Out; + zStream.next_out_index = 0; + zStream.avail_out = zStream_Out.Length; + + switch(zStream.inflate(type)) + { + case zlibConst.Z_OK: + inIndex = zStream.next_in_index; + zStream_Length = (int)zStream.total_out; + zStream.total_out = 0; + break; + + case zlibConst.Z_STREAM_END: + inIndex = zStream.next_in_index; + zStream_Length = (int)zStream.total_out; + zStream.inflateEnd(); + zStream.free(); + zStream = null; + break; + + default: + //case zlibConst.Z_STREAM_ERROR: + throw new Exception("Error while decompressing: " + (zStream.msg ?? "unknown error") + "!"); + } + } + #endregion + + #region Packet + protected virtual void HandlePacket() + { + + } + + protected virtual void HandlePacket(TelnetPacket pkt) + { + + } + + protected int HandlePacket(byte[] Buf, int Index, int MaxIndex) + { + if(Index >= MaxIndex) + return MaxIndex; + + if(telnetPacket != null) + { + switch(telnetPacket.State) + { + case TelnetStates.None: + telnetPacket.Header = (TelnetOpcodes)Buf[Index]; + telnetPacket.State = TelnetStates.Header; + return HandlePacket(Buf, Index + 1, MaxIndex); + + case TelnetStates.Header: + telnetPacket.Type = (TelnetOpcodes)Buf[Index]; + telnetPacket.State = telnetPacket.Header == TelnetOpcodes.SB ? TelnetStates.Data : TelnetStates.End; + if(telnetPacket.State == TelnetStates.End) + { + HandlePacket(telnetPacket); + telnetPacket = null; + } + else + telnetPacket.Data = new MemoryStream(512); + return HandlePacket(Buf, Index + 1, MaxIndex); + + case TelnetStates.Data: + { + for(int i = Index; i < MaxIndex; i++) + { + if(telnetPacket.HadIAC && Buf[i] == (byte)TelnetOpcodes.SE) + { + telnetPacket.State = TelnetStates.End; + HandlePacket(telnetPacket); + if(zStream == null && (telnetPacket.Type == TelnetOpcodes.MCCP_V1 || telnetPacket.Type == TelnetOpcodes.MCCP_V2)) + { + telnetPacket = null; + StartDecompression(); + return i + 1; + } + telnetPacket = null; + return HandlePacket(Buf, i + 1, MaxIndex); + } + if(Buf[i] == (byte)TelnetOpcodes.IAC || (Buf[i] == (byte)TelnetOpcodes.WILL && telnetPacket.Type == TelnetOpcodes.MCCP_V1)) + { + if(!telnetPacket.HadIAC) + { + if(i - Index > 0) + { + telnetPacket.Data.Write(Buf, Index, i - Index); + Index = i; + } + telnetPacket.HadIAC = true; + } + else + telnetPacket.Data.Write(new[] { (byte)TelnetOpcodes.IAC }, 0, 1); + } + else + { + if(telnetPacket.HadIAC) + { + telnetPacket.HadIAC = false; + telnetPacket.Data.Write(new[] { (byte)TelnetOpcodes.IAC }, 0, 1); + } + } + } + + if(Index < MaxIndex) + { + if(Buf[MaxIndex - 1] != (byte)TelnetOpcodes.IAC) + telnetPacket.Data.Write(Buf, Index, MaxIndex - Index); + } + return MaxIndex; + } + } + } + + for(int i = Index; i < MaxIndex; i++) + { + if(Buf[i] == (byte)TelnetOpcodes.IAC) + { + if(i - Index > 0) + WriteInStream(Buf, Index, i); + telnetPacket = new TelnetPacket(); + return HandlePacket(Buf, i + 1, MaxIndex); + } + } + + if(MaxIndex - Index > 0) + WriteInStream(Buf, Index, MaxIndex); + return MaxIndex; + } + + protected virtual void WriteInStream(byte[] Buf, int Index, int MaxIndex) + { + + } + + protected virtual void OnReceived(string Msg, bool bigPacket) + { + + } + #endregion + + protected long LastMSTime; + + internal virtual void Update(long msTime) + { + LastMSTime = msTime; + } + } +} diff --git a/ProxyMud/Network/NetworkClient.cs b/ProxyMud/Network/NetworkClient.cs new file mode 100644 index 0000000..5f882a9 --- /dev/null +++ b/ProxyMud/Network/NetworkClient.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net.Sockets; +using ComponentAce.Compression.Libs.zlib; +using System.IO; + +namespace ProxyMud.Network +{ + internal class NetworkClient : NetworkBase + { + internal NetworkClient(Socket socket, NetworkServer server) + : base(socket, server, 4096) + { + Id = ++_highId; + } + + #region Variables + private static uint _highId = 0; + internal readonly uint Id; + internal List GMCPModules = new List(); + internal int AuthLevel = -1; + private TelnetOpcodes CompressionType = TelnetOpcodes.IAC; + #endregion + + #region Packet + protected override void HandlePacket() + { + if(inIndex >= inMaxIndex) + return; + + inIndex = HandlePacket(inData, inIndex, inMaxIndex); + HandlePacket(); + } + protected override void WriteInStream(byte[] Buf, int Index, int MaxIndex) + { + for(int i = Index; i < MaxIndex; i++) + { + if(Buf[i] == 0x8) + { + if(inStream.Length > 0) + inStream.SetLength(inStream.Length - 1); + } + else if(Buf[i] == 0xA) + { + continue; + } + else if(Buf[i] == 0xD) + { + OnReceived(Encoding.Default.GetString(inStream.ToArray()), false); + inStream.SetLength(0); + } + else + inStream.WriteByte(Buf[i]); + } + } + #endregion + + protected override void OnReceived(string Msg, bool bigPacket) + { + if(AuthLevel >= 1) + { + Server.World._OnSent(Msg, Id, AuthLevel); + return; + } + + if(Server.Passwords.ContainsKey(Msg)) + { + AuthLevel = Server.Passwords[Msg]; + OnAuthed(); + } + } + + internal bool HasGMCPModule(string mod) + { + if(GMCPModules.Contains(mod)) + return true; + + string[] m = mod.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); + string n = string.Empty; + for(int i = 0; i < m.Length; i++) + { + n += (i != 0 ? "." : "") + m[i]; + if(GMCPModules.Contains(n)) + return true; + } + + return false; + } + + internal void OnAuthed() + { + Send(new[] { (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.WILL, + (byte)TelnetOpcodes.GMCP }); + Send(Encoding.Default.GetBytes("You are now connected to ProxyMud.\n\r")); + } + + protected override void HandlePacket(TelnetPacket pkt) + { + base.HandlePacket(pkt); + + if(pkt.Header == TelnetOpcodes.SB && pkt.Type == TelnetOpcodes.GMCP && pkt.Data.Length > 0) + { + string Msg = Encoding.Default.GetString(pkt.Data.ToArray()).ToLower(); + if(!Msg.StartsWith("core.supports.") && !Msg.StartsWith("core.hello")) + { + if(Server.Aardwolf != null) + Server.Aardwolf.SendGMCP(pkt.Data.ToArray()); + } + else if(Msg.StartsWith("core.supports.")) + { + try + { + Msg = Msg.Substring("core.supports.".Length); + if(Msg.StartsWith("add ")) + { + string[] v = + Msg.ToLower().Substring(Msg.IndexOf(' ') + 1).Replace("[", "").Replace("]", "").Replace( + "\"", "").Replace(" ", "").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string x in v) + { + if(!x.EndsWith("0")) + { + if(!GMCPModules.Contains(x.Substring(0, x.Length - 1))) + { + GMCPModules.Add(x.Substring(0, x.Length - 1)); + string b = x.Substring(0, x.Length - 1); + if(b.Contains('.')) + b = b.Substring(0, b.IndexOf('.')); + if(!Server.GMCPModules.ContainsKey(b) || Server.GMCPModules[b] == 0) + { + if(Server.Aardwolf != null) + Server.Aardwolf.SendGMCP(Encoding.Default.GetBytes("Core.Supports.Add [ \"" + b + " " + x[x.Length - 1].ToString() + "\" ]")); + } + } + } + else + GMCPModules.Remove(x.Substring(0, x.Length - 1)); + } + } + else if(Msg.StartsWith("set ")) + { + string[] v = + Msg.ToLower().Substring(Msg.IndexOf(' ') + 1).Replace("[", "").Replace("]", "").Replace( + "\"", "").Replace(" ", "").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string x in v) + { + if(!x.EndsWith("0")) + { + if(!GMCPModules.Contains(x.Substring(0, x.Length - 1))) + { + GMCPModules.Add(x.Substring(0, x.Length - 1)); + string b = x.Substring(0, x.Length - 1); + if(b.Contains('.')) + b = b.Substring(0, b.IndexOf('.')); + if(!Server.GMCPModules.ContainsKey(b) || Server.GMCPModules[b] == 0) + { + if(Server.Aardwolf != null) + Server.Aardwolf.SendGMCP(Encoding.Default.GetBytes("Core.Supports.Add [ \"" + b + " " + x[x.Length - 1].ToString() + "\" ]")); + } + } + } + else + GMCPModules.Remove(x.Substring(0, x.Length - 1)); + } + } + else if(Msg.StartsWith("remove ")) + { + string[] v = + Msg.ToLower().Substring(Msg.IndexOf(' ') + 1).Replace("[", "").Replace("]", "").Replace( + "\"", "").Replace(" ", "").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string x in v) + GMCPModules.Remove(x); + } + } + catch + { + } + } + } + + if(pkt.Type == TelnetOpcodes.GMCP) + return; + + if(pkt.Type == TelnetOpcodes.MCCP_V2) + { + if(pkt.Header == TelnetOpcodes.DO) + { + if(StartCompression(TelnetOpcodes.MCCP_V2)) + CompressionType = TelnetOpcodes.MCCP_V2; + } + else if(pkt.Header == TelnetOpcodes.DONT) + { + if(CompressionType == TelnetOpcodes.MCCP_V2) + { + EndCompression(); + CompressionType = TelnetOpcodes.IAC; + } + } + return; + } + + if(pkt.Type == TelnetOpcodes.MCCP_V1) + { + if(pkt.Header == TelnetOpcodes.DO) + { + if(StartCompression(TelnetOpcodes.MCCP_V1)) + CompressionType = TelnetOpcodes.MCCP_V1; + } + else if(pkt.Header == TelnetOpcodes.DONT) + { + if(CompressionType == TelnetOpcodes.MCCP_V1) + { + EndCompression(); + CompressionType = TelnetOpcodes.IAC; + } + } + return; + } + + if(Server.Aardwolf != null) + Server.Aardwolf.Send(pkt); + } + } +} diff --git a/ProxyMud/Network/NetworkServer.cs b/ProxyMud/Network/NetworkServer.cs new file mode 100644 index 0000000..dae01ff --- /dev/null +++ b/ProxyMud/Network/NetworkServer.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore; +using System.Net.Sockets; +using System.IO; +using System.Net; +using ProxyCore.Messages; +using System.Text.RegularExpressions; + +namespace ProxyMud.Network +{ + internal class NetworkServer + { + internal NetworkServer() + { + //string support = Program.Config.GetString("GMCP.Supports", "Core=1, Char=1, Room=1, Comm=1, Rawcolor=1"); + string support = Program.Config.GetString("GMCP.Supports", "Core=1, Char=1, Room=1, Comm=1"); + Match m; + while((m = _loadSupport.Match(support)).Success) + { + support = support.Substring(m.Groups[0].Length); + int i; + if(!int.TryParse(m.Groups[2].Value, out i)) + continue; + GMCPModules[m.Groups[1].Value.ToLower()] = i; + } + } + + private static readonly Regex _loadSupport = new Regex(@"^([\w\.]+)\s*=\s*(\d+),?\s*", RegexOptions.Compiled); + internal Dictionary GMCPModules = new Dictionary(); + internal World World = new World(); + internal List Clients = new List(); + internal NetworkAardwolf Aardwolf = null; + internal TcpListener Server = null; + private readonly MemoryStream strMessage = new MemoryStream(131072); + internal Dictionary Passwords = new Dictionary(); + private static Regex _loadPw = new Regex(@"(.+?)\s*->\s*(\d+),?\s*", RegexOptions.Compiled); + + internal void Start() + { + IPAddress ip = IPAddress.Parse(Program.Config.GetString("Listen.Address", "127.0.0.1")); + if(Server == null) + Server = new TcpListener(ip, Program.Config.GetInt32("Listen.Port", 4000)); + { + Passwords.Clear(); + string pw = Program.Config.GetString("Passwords", ""); + if(!string.IsNullOrEmpty(pw)) + { + Match m; + while((m = _loadPw.Match(pw)).Success) + { + pw = pw.Substring(m.Groups[0].Value.Length); + int i; + if(!int.TryParse(m.Groups[2].Value, out i)) + continue; + if(i < 1) + i = 1; + else if(i > 64) + i = 64; + Passwords[m.Groups[1].Value] = i; + } + } + } + Server.Start(); + + Log.Write("Starting core, version " + World.Version + "."); + Log.Write("Waiting for connections on " + Program.Config.GetString("Listen.Address", "127.0.0.1") + ":" + Program.Config.GetInt32("Listen.Port", 4000) + "."); + } + + internal void Stop() + { + try + { + Server.Stop(); + } + catch + { + } + + World.Shutdown(); + } + + internal void DisconnectAll() + { + if(Aardwolf != null) + { + Aardwolf.Disconnect(); + Aardwolf = null; + World._OnConnected(false); + } + + foreach(NetworkClient x in Clients) + x.Disconnect(); + Clients.Clear(); + } + + internal bool Update(long msTime) + { + if(Server != null && Server.Pending()) + { + NetworkClient c = new NetworkClient(Server.AcceptSocket(), this); + Clients.Add(c); + if(Program.Config.GetInt32("ClientCompression", 1) != 0) + { + c.Send(new[] + { + (byte) TelnetOpcodes.IAC, + (byte) TelnetOpcodes.WILL, + (byte) TelnetOpcodes.MCCP_V2, + (byte) TelnetOpcodes.IAC, + (byte) TelnetOpcodes.WILL, + (byte) TelnetOpcodes.MCCP_V1 + }); + } + if(Passwords.Count != 0) + c.Send(Encoding.Default.GetBytes("Proxy password?\n\r")); + else + { + c.AuthLevel = 1; + c.OnAuthed(); + } + } + + bool r = World.Update(msTime); + + if(Aardwolf != null) + { + if(!Aardwolf.Receive()) + { + Aardwolf = null; + World._OnConnected(false); + } + else + Aardwolf.Update(msTime); + } + + bool hadAuthedClient = false; + for(int i = Clients.Count - 1; i >= 0; i--) + { + if(!Clients[i].Receive()) + { + Clients.RemoveAt(i); + continue; + } + + if(Clients[i].AuthLevel >= 1) + hadAuthedClient = true; + } + + if(Aardwolf == null && (hadAuthedClient || Program.Config.GetInt32("AutoConnect", 0) != 0)) + { + Log.Write("Connecting to " + Program.Config.GetString("MUD.Address", "aardmud.org") + ":" + Program.Config.GetInt32("MUD.Port", 4000).ToString()); + try + { + TcpClient t = new TcpClient(Program.Config.GetString("MUD.Address", "aardmud.org"), + Program.Config.GetInt32("MUD.Port", 4000)); + Aardwolf = new NetworkAardwolf(t.Client, this); + World._OnConnected(true); + } + catch(Exception e) + { + Log.Write("Failed connection to Aardwolf: " + e.Message); + } + } + + if(Aardwolf != null) + { + if(strMessage.Length > 0) + strMessage.SetLength(0); + foreach(Message m in World._MessageData) + { + if(m.Clients == null || !m.Clients.Contains((uint)0)) + continue; + + if((m.Flags & MessageFlags.GMCP) != MessageFlags.None) + { + if(m.MsgData == null || m.MsgData.Length == 0) + continue; + + strMessage.Write(new byte[] { + (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SB, + (byte)TelnetOpcodes.GMCP + }, 0, 3); + strMessage.Write(m.MsgData, 0, m.MsgData.Length); + strMessage.Write(new byte[] { + (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SE}, 0, 2); + } + else + { + byte[] data = m.MsgData ?? Encoding.Default.GetBytes(m.Msg + m.LineEnding); + strMessage.Write(data, 0, data.Length); + } + } + + if(strMessage.Length != 0) + Aardwolf.Send(strMessage.ToArray()); + } + + for(int i = Clients.Count - 1; i >= 0; i--) + { + if(strMessage.Length > 0) + strMessage.SetLength(0); + + Clients[i].Update(msTime); + + if(Clients[i].AuthLevel < 1) + continue; + + foreach(Message m in World._MessageData) + { + if(m.Clients != null && !m.Clients.Contains(Clients[i].Id)) + continue; + + if((m.AuthMask & ((ulong)1 << (Clients[i].AuthLevel - 1))) == 0) + continue; +//The problem is between here and next comment -- or at least this is what controls the GMCP shit that gets sent to the client. + if((m.Flags & MessageFlags.GMCP) != MessageFlags.None) + { + if(!Clients[i].HasGMCPModule(m.Msg.ToLower())) + continue; + + if(m.MsgData == null || m.MsgData.Length == 0) + continue; + + strMessage.Write(new[] { + (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SB, + (byte)TelnetOpcodes.GMCP + }, 0, 3); + strMessage.Write(m.MsgData, 0, m.MsgData.Length); + strMessage.Write(new[] { + (byte)TelnetOpcodes.IAC, + (byte)TelnetOpcodes.SE}, 0, 2); + } + else + { + string msg = m.Msg; + msg = Colors.FixColors(msg, false, true); + byte[] data = Encoding.Default.GetBytes(msg + m.LineEnding); + strMessage.Write(data, 0, data.Length); + } +//BLAH + } + + if(strMessage.Length == 0) + continue; + + Clients[i].Send(strMessage.ToArray()); + } + + World._MessageData.Clear(); + return r; + } + } +} diff --git a/ProxyMud/Network/TelnetPacket.cs b/ProxyMud/Network/TelnetPacket.cs new file mode 100644 index 0000000..9920e90 --- /dev/null +++ b/ProxyMud/Network/TelnetPacket.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace ProxyMud.Network +{ + internal enum TelnetOpcodes : byte + { + SE = 240, + SB = 250, + WILL = 251, + WONT = 252, + DO = 253, + DONT = 254, + IAC = 255, + + TTYPE = 24, + MCCP_V1 = 85, + MCCP_V2 = 86, + GMCP = 201, + } + + internal class TelnetPacket + { + internal TelnetStates State; + internal TelnetOpcodes Header; + internal TelnetOpcodes Type; + internal MemoryStream Data; + internal bool HadIAC = false; + } + + internal enum TelnetStates + { + /// + /// Just received IAC, nothing else. + /// + None, + + /// + /// Have header (what packet does). + /// + Header, + + /// + /// Have type of packet. + /// + Type, + + /// + /// Gathering data right now waiting for IAC SE. + /// + Data, + + /// + /// Finished packet. + /// + End, + } +} diff --git a/ProxyMud/Network/desktop.ini b/ProxyMud/Network/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Network/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Program.cs b/ProxyMud/Program.cs new file mode 100644 index 0000000..801d342 --- /dev/null +++ b/ProxyMud/Program.cs @@ -0,0 +1,151 @@ +//#define MONO +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; +using System.Threading; +using System.Globalization; +using System.Diagnostics; +using ProxyCore; + +namespace ProxyMud +{ + class Program + { + internal static ServerConfig Config; + + static void Main(string[] args) + { +#if !MONO + _handler += new EventHandler(Handler); + SetConsoleCtrlHandler(_handler, true); +#endif + + // Set US culture so config and other formats are universal + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US"); + Config = new ServerConfig(); + + Stopwatch watch = new Stopwatch(); + watch.Start(); + + // This is the main loop of the server program + while(canContinue) + { + // The main loop time + const uint sleepTime = 5; + + long time; + + // This is the main loop of the game world + while(canContinue) + { + // Get time before loop + time = watch.ElapsedMilliseconds; + + // Start server if needed + if(Server == null) + { +#if !DEBUG + try + { +#endif + Server = new Network.NetworkServer(); + Server.Start(); +#if !DEBUG + } + catch(Exception e) + { + Log.Error("Failed creating server: " + e.Message); + Shutdown(); + break; + } +#endif + } + + // Update server and everything +#if !DEBUG + try + { +#endif + if(Server.Update(watch.ElapsedMilliseconds)) + Shutdown(); +#if !DEBUG + } + catch(Exception e) + { + Log.Crash(e, "server"); + Shutdown(); + break; + } +#endif + + // Now check time after loop and see how long we should sleep to fill loop time + time = watch.ElapsedMilliseconds - time; + + // Need to sleep some time until next update + if(time <= sleepTime) + { + time = sleepTime - time; + Thread.Sleep((int)time); + } + else + Thread.Sleep(0); // Sleep at least 0 every time so program wouldn't hog CPU + } + + // Loop ended save and shut down + if(Server != null) + Server.Stop(); + } + + isFinished = true; + } + + private static Network.NetworkServer Server = null; + +#region CloseEvent +#if !MONO + [DllImport("Kernel32")] + private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add); + + private delegate bool EventHandler(CtrlType sig); + static EventHandler _handler; + + enum CtrlType + { + CTRL_C_EVENT = 0, + CTRL_BREAK_EVENT = 1, + CTRL_CLOSE_EVENT = 2, + CTRL_LOGOFF_EVENT = 5, + CTRL_SHUTDOWN_EVENT = 6 + } + + private static bool Handler(CtrlType sig) + { + switch(sig) + { + case CtrlType.CTRL_C_EVENT: + case CtrlType.CTRL_LOGOFF_EVENT: + case CtrlType.CTRL_SHUTDOWN_EVENT: + case CtrlType.CTRL_CLOSE_EVENT: + Shutdown(); + break; + default: + break; + } + while(!isFinished) + Thread.Sleep(10); + return false; + } +#endif + private static bool canContinue = true; + private static bool isFinished = false; + + private static void Shutdown() + { + canContinue = false; + } +#endregion + } +} diff --git a/ProxyMud/Properties/.svn/all-wcprops b/ProxyMud/Properties/.svn/all-wcprops new file mode 100644 index 0000000..cbed7e5 --- /dev/null +++ b/ProxyMud/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 41 +/svn/!svn/ver/2/trunk/ProxyMud/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/!svn/ver/2/trunk/ProxyMud/Properties/AssemblyInfo.cs +END diff --git a/ProxyMud/Properties/.svn/desktop.ini b/ProxyMud/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Properties/.svn/dir-prop-base b/ProxyMud/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/ProxyMud/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/ProxyMud/Properties/.svn/entries b/ProxyMud/Properties/.svn/entries new file mode 100644 index 0000000..3f7c3f7 --- /dev/null +++ b/ProxyMud/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/ProxyMud/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:43.164138Z +eb69874c56b495a6b43efc4f598952f0 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1452 + diff --git a/ProxyMud/Properties/.svn/prop-base/desktop.ini b/ProxyMud/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Properties/.svn/props/desktop.ini b/ProxyMud/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/ProxyMud/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..7abfb3e --- /dev/null +++ b/ProxyMud/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ProxyMapper")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("ProxyMapper")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("4ee0393c-9059-4583-9bc5-aeccdc239008")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ProxyMud/Properties/.svn/text-base/desktop.ini b/ProxyMud/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Properties/.svn/tmp/desktop.ini b/ProxyMud/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Properties/.svn/tmp/prop-base/desktop.ini b/ProxyMud/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Properties/.svn/tmp/props/desktop.ini b/ProxyMud/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Properties/.svn/tmp/text-base/desktop.ini b/ProxyMud/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/Properties/AssemblyInfo.cs b/ProxyMud/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..977f0f6 --- /dev/null +++ b/ProxyMud/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Koop-Proxy")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Koop-Proxy")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("4ee0393c-9059-4583-9bc5-aeccdc239008")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ProxyMud/Properties/desktop.ini b/ProxyMud/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/ProxyMud/ProxyMud.csproj b/ProxyMud/ProxyMud.csproj new file mode 100644 index 0000000..8149d3b --- /dev/null +++ b/ProxyMud/ProxyMud.csproj @@ -0,0 +1,99 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {B5F8F4E3-630A-420D-8461-420C36BB865C} + Exe + Properties + ProxyMud + ProxyMud + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + False + ..\Resources\zlib.dll + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/ProxyMud/desktop.ini b/ProxyMud/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/ProxyMud/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Resources/.svn/all-wcprops b/Resources/.svn/all-wcprops new file mode 100644 index 0000000..b2f5ec9 --- /dev/null +++ b/Resources/.svn/all-wcprops @@ -0,0 +1,23 @@ +K 25 +svn:wc:ra_dav:version-url +V 31 +/svn/!svn/ver/2/trunk/Resources +END +Jayrock.Json.dll +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2/trunk/Resources/Jayrock.Json.dll +END +zlib.dll +K 25 +svn:wc:ra_dav:version-url +V 40 +/svn/!svn/ver/2/trunk/Resources/zlib.dll +END +Jayrock.dll +K 25 +svn:wc:ra_dav:version-url +V 43 +/svn/!svn/ver/2/trunk/Resources/Jayrock.dll +END diff --git a/Resources/.svn/desktop.ini b/Resources/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Resources/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Resources/.svn/dir-prop-base b/Resources/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/Resources/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/Resources/.svn/entries b/Resources/.svn/entries new file mode 100644 index 0000000..21a7e3a --- /dev/null +++ b/Resources/.svn/entries @@ -0,0 +1,130 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/Resources +http://proxymud.googlecode.com/svn + + + +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +Jayrock.Json.dll +file + + + + +2016-03-25T22:18:43.180138Z +459fa765f9ba953efe528a01e8db0646 +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + + + + + + + + +86016 + +zlib.dll +file + + + + +2016-03-25T22:18:43.180138Z +df39bd70ee5b2357b65256d75be7640d +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + + + + + + + + +65536 + +Jayrock.dll +file + + + + +2016-03-25T22:18:43.180138Z +17fa422f0022b511cd53b04ca946e74d +2012-01-17T10:55:35.805502Z +2 +default8p@gmail.com +has-props + + + + + + + + + + + + + + + + + + + + +86016 + diff --git a/Resources/.svn/prop-base/Jayrock.Json.dll.svn-base b/Resources/.svn/prop-base/Jayrock.Json.dll.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/Resources/.svn/prop-base/Jayrock.Json.dll.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/Resources/.svn/prop-base/Jayrock.dll.svn-base b/Resources/.svn/prop-base/Jayrock.dll.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/Resources/.svn/prop-base/Jayrock.dll.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/Resources/.svn/prop-base/desktop.ini b/Resources/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Resources/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Resources/.svn/prop-base/zlib.dll.svn-base b/Resources/.svn/prop-base/zlib.dll.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/Resources/.svn/prop-base/zlib.dll.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/Resources/.svn/props/desktop.ini b/Resources/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Resources/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Resources/.svn/text-base/Jayrock.Json.dll.svn-base b/Resources/.svn/text-base/Jayrock.Json.dll.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..eb1a3fa9b596b20e97b48df44378550a30728025 GIT binary patch literal 86016 zcmeFad7M?nwLe-_d!Mn#)5Gb3p&RIiO-~Gs%A^7!lNbq>H$(Aret*RV~9vipQ+y>g@&U+&4pLUkmf zB7MxRPD|qNvij~qL|0qRtx^Q5LUv-D5Wxy%C3=9Iz#=^u4OT^i%CZw@QiyFO$|z(s zf!w}IbTV06;Fsm`Q%(pGti(0oS|M`#T7otZC|`=#tzEotA|5hQ!3qZpb0pM+>aen$ z00;<+s9*w02nH+iD!_~KE;f0qMg*Whs=geuH4)GxM5MeQGC1zY8`-xl4I25LJ7Sos)NlABisS(Q|RCngIf?>5m1ffB4Q`#`Yia7F18jz*@Jrl)lQ~ZlUNTYSpRm)`)5qks zB+dY?{{7*+$O`OF$y_^TSA}ABA_wF{!F8l7R#=6QXgW%KAbJlL`O1}%>3`CeS2u{z#IniKZFnq}b^`;^4tyWeOq_8U}FKRux zagl@lGt^b;p?-tH@S_?jda{w!uoD_|+XU}ZA%=3`3nqqwH}~}nHyzWh#Mu-88u3iY zPib_YM<3(Is#fs|wH%qL&E#n7q6>2lcvy*ZnG^BJG-;F*2cP(o&t?2`iA-9_&H3x} z!+bJ$yh6!og9^qQtN)^?xky6M5%A~ zQp3i;{MoJBNh6b>6I;L_c93TPJYa^*Y{NFQjetAbumfbuy!^d>d;qG^e>y99}maEZ1FQ9Rl$fh_jUq(8Qg!=miq+e+12ob za%GwdB_>jK7qw;l=BXJcV2Xkj66uw2MEQl82Z|dEbgt(H1)|TW_Vq}16RlPtMSD5Y;^~zo zq41q;B?v(R$~;^!Jv|({X>tGGdwEg)Rhz~@J^SZgue1_ZVL+*tm<=z2XD!NT*Tyhd zF9u?E13!WwCbVCxP-+_kUrmLg!P&K)Oxom&$ism$qX}62;J`;x@%m7_G_ALRx1{K` zM6Xmv+Y2E;&&m+kp`oC=vUme1%KN%pl`{@ zAGHRy*^WDyQW~D%$B%LvK;wy^vjV=v8jiv;d2v{$Rq2{bW0cC8&pPqVeK?24h}MG= zy*&P_!O*0n&??xpRYXdBo!mm_D$bB};MxLT?82I~WVu9zaGk3}9i?%u<|Hg*wVO{W ze@cUelAFd#TuF&Iyr_ukKPOUSsCk`{cij0MOK3n(|gM53*X=ym*V;9ikr-6UI``XayDYMfXx7! zXQH_J~#6lPm735T!rph^K zc^ZmKxrJ^fwpKH_C=%xlJ4^dM$eUt#_Ln6w=?i9*r}@$^r{u_$RqJ_WQRnp(llx!rSaxd&LX72pb57p$G2MTk#)CFN!<~+S~<;V zGge{=fVRz@bhd)V<@WJl#!S54h=*w)@1^F!vSPohzrpnbSUINB}qoJd-yte4m z5kDP|b)W*AL%_`;;GnCUvZm^Sw3Wah66GF;V3a8!rh|7Eql*epx0uTyA{k7kana(@ z$}W~-qYlNOD@HljU;?8=G*~blyPVEyc&!9Ruc(9ZDcb+P*fye;`GMX@!?YP>+M^|U zm@uld0bc%4?OllJTnncslHH8Ix{lqku5Enhn;eDf2`!Z$i*sU5ki*|r*LnP4a`50{%76 zuR2(o1oWZ=`RO@qo;drWj_0t@89(X(T9;YVeM=Xa%iOjc)V|z4g*6=|3OKjx1f5`m z&>IB0ky+DO3AB!An{`}TWu=~=H=vQosIAv=W*QhwK;h8@>eH<3%tZay9Zb}1Q%YUN z&f=`e?aN3qbyb4eL+tZhnpxR{pMs1faY!l7tzVCf%9EqeLgJfelLn|al_$r8P~ay_ z0in=Om<>XapD-VUVn3k{gid}!0|=e{gf$>^@e|gA(A7`a3_?jHMfp5;9Th>LRc}L( zu~-ST=jd8giygm{{e4U@cQ92_n%IB{*lO+@tiKMv)hbjlJ+TqK^f1Qt69tCL zbAwe^8D+(S4jDos&5H$Bk&4EhABF0mQ~m|SR zj8;x8P3OO3N+0?U;bEmC`R~BeBcBoOtHmjX{QFY!6zkRQ^^hZaI3puD*A~kn^*4~L zDjp(h5slx7Awec?gg09#Rs|<2c5VVG*Ukw9OXknQ?~*um`e=?F3piUSD3lX&w!#Cu zMeRt4CuG(>nSrIXB;-ES&)xp7_h0I7L8{f%A8HT@MU55Thy^!#bUb+nB)l(JkbIW^ z7AF6}e~Xgk0iv&iuJoAYv;92Ik@cC2IHq2$rYwrt%EmM@34PNN&l}+=6%2*DivyHe zJ=%Kpn3lYn*zzpe)(f>hU|k!b6QrHgeGRq0N4?)i?dMVN^HKYH)O&qYUL!K!2Yggs z0W$SFMCCP|&eFH0LwTsI5mPQ(uNS>@B~SlrDQD*)VnpL-icWHNM5j`9L?>B0qLZ{P zJtdFQ@>pmptp5TdXYzLFRyp@T9vs-ARj{6ET(MGnu~y<2wAlSFB4IQ`m~X%T0JR%F z-g54yLK*(99;lAEHcmr#$3xrZy#ePzkC*a|^=CprYBBG5?aXp*0F#zdv&NJYoq^|- zzVWS5(`YRF4P@y>V;MN%+OgMS6T6TprUKu`pR_%2mXYdJKgi3?hQRtjfF$5o-IDBO`x$v zF@YfKUtN$aM6gyoYs%7^!j{t$R2M2_C4K@=h}va9z>xDpe7m)>9jRQDvl|Jp^Q1lc zY_-}+u0cjt0?i%uOflGNbB#FP2G!O%bKlJO<#S=!lixXtbZ$F$^{M!z`apnY_^_u@ zS8=3szdnEJrL{%W@JAoXbAF}+Xf1RHtT*__QhVjTQVyoJXqDt8b|RN3dG3WLEqK#n zY|2gNDoqau6R>sBaESlJGcZAm@a(h!jQB+HzF0ZPMVN}y9l8zeTsvsGR_tU)w45YG+TxoPp-^nui?VrjATJ99(WmxK;v zZa9FG%V6N9#r>_kN;?n>-kBSTg{wx!!U-6_s8y2>-^1)nOI}7A<_-+vBOq3D$#-nqo@7{$Op#K zPg@K7qWf6te2oSOilcNg;bM;uy8o9=b1EKdf0*Lo^7YeNr?Wl7p>Q(;{Xgm)gqrfG zyXZ9*YUO>+b!L5QLv(QZymXEEcz?=0fSQw|(>3yopIpziqr_uEc6CMko0woEF9j?rL5t+=*8vd>#vYh?BME+2bHi`Wd z?~5POD=Z5?$$y|^77vb!%ZI%D^RVQsh#PKZaJntI^D1Q}Yoa(Zd5?R6PRpW#yV3=J z4h2VZ#)xKPXfL8a@h0>q`h$*6ZQR%I8Yy1l7f90)y~U+_`Ez_L@k z?Zm4Tf#x))D&udMgKn#^KJDwlP8`%c{5F%?o|S+_H*LvxZ_l4U$4~{j^=dt$m9)zv z@fsMVqH&!UcM#7?{H|3*Y8xHLd;Lqsd!tP}JGGzcF9+Jfo4%N*&KL6;UqMbC@6eZw z_lGYT@9>w5_s2Hz{QJGWzGo+n_#;yL>D*C$Krcsq5y@V5>M&m7PrhhezqN>mr5yJB z$+XcrK^r-2_~dH~pc=`dB*+Zto!myUQvLs=Z9>@1C(ogc3AAzF9S18pkZkR@5s>Hn z84M^*$Kkdw+#4$~w|I+FQJW2ke<80SBx~uq+M{yGti>gxrsI^OWYml$nlf_+HjaW$ z0rD$dcaaoJh!9Ux@g;rAOrq)8B3Lj3V;tT;4OSI`O7TD~NULZhE*(FGIZvW8H;lx;nKR05YsUw55__~&wm?*&yD6p&E^)Z`MN^~=RgOz z7llzdTKYpIDJZCa7k=j$oM?`qu`Rm9jEgi!rAHNCQXN#-JFABJIG@rjm}_1_Hvsiq z(h^u^^P>u86wiW#-L@I3v=Y&#bL3gmOJXy?n8qM)Y~gu}-bbUvSRS0DJO|@91HY^B zyA8kH_`$}BzvGt;j(zaE5WhP7=yB7be#K1uR^oR%em}+!hEMzhKQu6bti{*xyAnTY zOMO76rzGRX*_ck0A6S4|asC1gtfOl3$O}C(Ra=K;;FSuMEnBk`2at!Jeyu|n(c`|? z-Yvb&Lo)c=Yj^4dNdJF=UX6Utg<8I7?@%RrK%!_6v*&*B7}-!l~m>9ou|2emHbw_95&4hJ@JixChg} z#0T)FRb~Y(+|7Rx!X-Wgp{LnI9e^6IeaprrqX$9t?ZdUMZ`}9ku)a@Ew*~Y~_m8l@ z;fVti>wH9Fg)H2lBcbAahK3yTp@-!l zrRTJ|1I*FV4i`p~^U}mJv-5Zv#PF67W?vm+MHf*xxy`(t#@CcwmRplPxom#VVwA5Q zJ_h&b0rYg;!O|-8ylPlQ)X@o83LGD$?j!vXhfJ^nrYfjzX$~_6JPU556xg%Zg|4>Z zm9pT~9bUZ@Bi^{y@kj}2IEl+eJK%_N!>%2t(_tu3jl`Z0UYeLy zyoq>&&T=sOxQ>0(;>5p59^#<+(2XNKcGxs7(>^EW5tMmVCZ-YOQH>-LHXjG0%zQ&h zOI*$(^KAsmFLY}Z8fmq*;rjt}S6`8hxb&XvsH9C31`(9gUq}-bzZ7-gEjp`2U6R|5X_H0uAA4G+t%t1#OU z|ItiZkE`(n<^rZKeX$ImcPPW!lOQK1`gr8ElYz?4$M?LxPsyJk85tP`oMct4G<6lc?wVpRVnR3z^X@n2li z!DB)V(r{m5M%Dak8d(b+sbB^hrYNr4tOl`QhGRhx>I7sfF)I)kAwN}rHgbqsRg0`# zJB~HVbXt_$nHE|>EfzPW2Zs~XCUS$7*|8wswa&n8dln1$3~;V~_i&e3P``YIqYNBj zgv`rVArnupda&-n$;M9HpGRX#z}Cn9CCz=c|#>_YOcu>CS7P&f%HQAT)pTj+P~^fh-rZEK9g<6D|0I z`|}Q6F7Fwv9;|dC>NB;~*kh#Nu(h@i;V$On8;6$Eop3{W0Lbj`Sy`h~S~Ug9wV2!( zlsX5;g*v*aj@#sX`cq39 zEWH-ftuJrwxfZpki@^DgK%TspkzoAAjG8djvuiR8AZ66P zW?~u{VXyho3g$CZt`u~RR6Hz0~c)3?s0Etzb>1OM!;2bOqH zHClx?B1mA&t>Qd3U}uIO%*tR+THRSE+;NB<`&sJFI^Im> zF#nx(V)5nftmnA0I%~j3Ib+eXn|hIyG~e~f(5Q*6X|PQ^*R{k_j}Pl-_9^Qu6E{;S zS;34Z`lD2$85whyCQbY5^3qJ#X&lNU^)95>_RO8c0GhI{x7K$Op}W+b3FMNxlSr@Z zYW=!>f!65$_#NL#q@2k1(wxAjD`y?|44PMVb@k$%*#(o&q03gNJZB?vO$2CEB3;{J z0ET$XL1=wRA=jcs;MPbqSUkNxh*;UuD(RjG!PhgM9?uwv>81#$sR*C{rHAQ6r=)w| zs+iq%#`!^nosS>PotTT?4frMSYr>DB)x-v_)80Ncok5sg+hCQ<*37N2{yT6RVuVVD z)<)U|Rj#p;`%#KSn53y(Y9(Jzp*dFaU;kb%i1>+O=L(eD}*z%EHQXepx%gXREXtSIKC zUev}e=?zpOuKj`MQPhwDX99$*T15&^&LF_9zu;bPTvG6bvos}6YAc_C3)OB!aY_>gbRT>0fkM6q%)|F z_^vK?w9m1vU?PKB0JcJc@MprG7OD>GZ!=UDV{G~@T(C`+_*s?G15 zrJ3k@wqq6mmHELCF4gZq#6%2AHDv!j(qSwlz90_G3Tp2*ZO&q2N5v<(Q>zp6N*p$f z?3!W&`Vz=6fF?DyxUEIACikVkK19Q0J~<90Yxgc3^6Z zo-TiJebSnFEXs#<^J&^4RT~+}#N+Vr-fza?JY2sEIzI$qb-Kc(2=+xtU-aHE-A>Q0 z6;TY3P$m3li|F!{8Yx1nhh%j7m2aQ}1)@Jlc`UFI)aCKl7sQUWe7o^8@JGz--woeZYpFaJ0 zl%8U?Pk+gmNe>$y-Apmtr$?JPeg2rwL^o5+_UR{nne?M5J;iLF9<$)n=Rca#Q%s%S z-)7^^bG~}ZH*G-oS?m#Gvz4x<48 z-q~*Vl7F%=la`*8>;SRQcP-K2qk18V6Wb_mGeKe52!&PbGuShCrli!4ErHy`Wsog7 z6AbI01;2GtNyRWJ3y*y-t2Jg~_rfw|L2Ej7jbQEY!(qwV3?A7fd5o z^2t;%jUmYcUN9{OlZR8m0|~#A!nDXQNxM<2Exwk5@#4W+BHtG*F$H~ecT5_`k((@RW9u#cmR5Til!1- zBA^<{Md2ylQWNh$x!VGj`JR=k=krf}JXw{k^ciGrNY(d3J~+0-wF7)`O4a{HLome0 zsg(IRl`fsc%f69yLzMHHp zMZMUiCeO*jS`7W~fBI#p7%GIzcDgS^kycnW)Sk7icwO|CU+&vbWIt%3OApo=ccZix z*KBnx`AtZ!uO90A+17zZ*Up>UvX>ZV#RAlsK3q){qOF))|M7l3aLD@; zC?qZ96p^9C^0AVZkJWg!2N!343oWiTLqCp(ixI_4Nuu~1Mp%c_M1;yJ#6S`MnutyU zB;-Ccm*{Rl@||UR>gpHJohg%f{v{Pz|hpy@S?TjOaFRQ_M(t6XR{tl&TA4Ed%2<1mm>LQ zj{hK{rX9{i(}6MX==`~BSCOgbpl;3U@`ep`*UmH-9tYYYOe3tMi)Z2!3q%_ATqIbZPNyQ>l5D`^@!KD6dxQcA(`|o0NQZJ zco=n1CbNIw=RvbiJx^`RqmFq{_!y7Jln!~!J|z!~jF5xE$9X(1?vTeNr{sb01Ux8w zyvJi|hdeGlB@fgFcu@G)JRYzo9q41?DS0es9uz*oI5_{*k*RNId2#t0R+uScs*a9NZ61A1tU&&%k!W-|Dq5Wpuu~rus>s8u z*_aUE{xiqG7=~{Z4l!@Np&Q|Ik9GpzGr@be{B5G@l0YcY6PP!Z=)lEx{zaX{3hQ^g z&3N_mj^9<&uX!I%!xutPloLpJh-`uW7FCTHy+U6prngL2Z1uJRObJ{6p|NWyNGnmk z17CCJFW8o}QJi1`)0Ak#1dy@gm0SNQa>98I2Za(2!mwTO35X%sE?dk8A8P-q5z7>t z-+{ul$;uHL>v#Mur#F&SRU{9=0H!kJD_iC(r}v;~_>PhI>SesoPc;`yALR1SEsV+sJIs&j%&&Mgk#u@pq}L_bffcJDce{5QKcgy9iT_ZseeX2p1_!)10^AoHhdk?nFaK_1MkM)03}XYjZj@)F60@L6y!co_>P}R`<I8J}cYj(D+{tjG0f&sfNr^2b05SnpGVR zhU)vkix(*|50OCF3N`ixVj3R&@eMR^|6Uh=BZFxHx(`S53m%j1h4}g{YO2jAQi%1Z z5}$)DD=})>!D_r(hel3n*_srBSft${H*gG%YV z^)j!a^mR8XmrM8U>+pOaMPCld`E#AE<_Y)=2kl6XCS&E z@&FG1Iz-@a-I$wzKkeXZ6ZS67L`r_uyXhib7QIf)5aXtGw5O!swZ1MTV^zSqX)#r( zT@}FX7x>bGA-=Df&P$?g1IrY^c&g<`sQRk(jQmkQP;#s1UwFmt*Vs&iO?N_G< z)aYQ&5N#ro4q&@PbI6qa#YsQ~Vxm9L{>ylTZ8G|L(WftBmVmuR<29F48^{PpanQ)h z_@c|Hy8RW4(hm1D^}P8)5kF zTBkPONv3@0{byW~<*#w1z87Y#Amvn-@@yyhTeK1XeE2f-82uuHZoiqt2#;=#?yOkX zojd;8bVZ07n3edHY#_egA4r^wj7?wn#xWD=1R`l!ee)Dui;_~&cp28&3S1o-NU9}U zfQ21Dcu#g|E@qqGhBlmGZa%*Gw=ewk?{4|$CH=g4$xyF8eX?}?-C;_E?+)t()OP1W z%I@e7`fT4y42OuR&kUzkx{Y)?b-=ctry7CCU$&7VAKgw1ME|iJx6@Sr%5#SK%9L7% zHlaDti4ArSFkxyn&Nfm|Tbdg$_04DH(A~nSZi??8^4UuAIaH9Q^Ijgml7PzubU~3L zxc#5E1yK$@N#IKf^(-c}0r#E&pCZ(s%hI=^4}f?b(a$&qoq9`0@v)23A$^daZg~pk z(+z!bNec`4seQWrflotJbtt(HN~jF*{X#dQU%v$oP2F1SH}27V-&{AmCDER=K6fbVnRrNG001gI?&&1?~_;(JvMuNujBSki2}|Poq)>i z*$69f9<^Tg8{Dn)qG4I@xpYRq)%6dL7Dez7ZR|x#zFIHM^e>bzwN9`bC{4hb>5JbD zd0bE9(@02%7b#~Uj6TJEJ|M=wQTU@bMiGzp7f|iuM#OhUbNs5_*g>Q?W62o@CoQD^ zbB*+HT-_Je|G5U*qO4H$7a&-d?ThUO?=g{`jPg{L+Yil!Z$`eY=gfTRp8hKqEWlj7 zrQV@!9amZP_|H;;&ZS&>svH0Oqb$#BLstEG2DH*Q**(LxGfy|)TuW}L``;bM=t1@h540rlyw zCxB98ClAG}sTaA0`PaLx@07IO%Uj~fpi=KV>GKsETV}kWm#r`g(`H^1c1YPK2I!nY zY=cbR1^;_cLHxR6v~sAq2&5V*KGsoRLt0#I$LHWDz*ij)2XV!3BE07PzKh_c4Z{@> z-9NZLflpUF=(dseZHo2{M?;QpksJ*LXoT1z#90FB4*eA0eazAa-)UB;)mI(yT1C4< z?m%J`XQaoJJkhC-XDX0`eyGkp`;g~QYvZxG(1a`6P(owH)kErSmC|a$7Hr#ZuVCb3 z9r}1#O-%ILN7cwlc}()!AJY=QqA3K)LVf0TY8Omjfn^Y0wxfIJWfVhqZnruJp zP39?zk#Bk`jpcdPo z!${%&?>D!}FEgc9U1OAAYH!D%(+gDjs2krmeH*EH(LMQ+QX?*hcXs9V!gT|C4IJ>c z;tXo2G$v4AM_WTXbNMt4DJ2Rx8+8J*6R<>L zJKHN-H(B(Wm;RqG(ke^Mx{1binvz~jL9}>ML7Rtqvp#(N)GH>W#AJ>%UUg1Kb_4(a zfU9eV_G)P4O4)9VT-YE8HsRa{_vD?;klMN1S5{1M_)(3QAVzi2p>3xMjp)TQ{NGym z#50BA4>2(joO=*2v6DGbJ;L~kJEyFiNuaXlQL?(9{Yt1#y60LkHPxH~98u%xjz zM>P^RTKuEE-=fL0+K_J~GLM}2I0M$#=I*jdiX}W{RN6l?S%DEan3x7}X#KRsi}#EOR8%CD{K2lQvcCZj_ph*g~pPXLjt`=ujNn?hc_ab6+lW= z=u5W1xNPbO8Qax4N_`%@0a@378%`6^R3o|C2H3vu<5OLf%))1zU-rtZ9NIut&dqS0 zJK<1&w&$aoV1wTwhljpS+> zExH#W?((;ns+#m*$&i{*u;h}O^2$EC9esful?8qQlhJ&)QqjuW3W&T~>v4`J;}%aw zqNzr5Poc*~=y6~w8{X7b&NeXc^l0G|y1&c6Ora!ySbUw^604wei?F?XdT zRL*viz*pUEba}Za$G1E=h^89Jb#sp^~PW!piQZzME=$__gKt zCy%9sI+faJf;|yZv{G9edFOfZ-tNguG}TD1t2h7pUiT6`cDVB;{J(U{X02E}UwRhh zyNAlBMKiXkPwi?S0i~{}DGinMsh#Sh!h}U@_6PAD)n5dw->rPARS;TBJnpvaU`a*o zQD1OLpPDTOk{PZ1r4N+!ftl?=|62f?StCz$$ZVVfBCjD+=x_DWf#c~f{$rAo(sPEB zm LS_%Gs+bfUdY(fSdT|%L;iJq*ij_#mXKHJ6POFGVQXTS*R5rxMbD=`xUt#|1S zaApLWg`kG{82;RP@#bRub@&4q%3FVPlw^7LjXjvD)0m0(G020(1RaJ)tNm`c6vl2Xx`x26O)N03+6S{&z`vA^18+IW{;b@Dw&w~_1VbmoJ$;X+hnBx%CU&R2)#VY(Cn*7%_PqcAh8~!E3#% z`qlKS8Bp6F69PN|Q3v;-ui^IiS|Rc#;H4Q1<~=4iBo{BeqMl+5U4tJA-Q(iPV&yIT z?ziYM`r@&eLx_#QJFwx@V?Pi(A--jC%J**-tD27{muZ?W%m|#e+4&Fj7}et9>jl4MllCxP@FC7W@ZuPt}Kf4`>Y}9IpQDL$FhS$#mZ%l z$c-J#j*9vi$@Bf#j#yOu3jU}F<&fK*-OJzx#l^Wh@O9&b>~6><{%_{KoEsGP<&osO z*&UEiB?=Y1oEsJA7xXL$iphnP`-4U0GE<~?8j_wVhQN)BC7ot>ii>q{GsWedN%EDQ zDdvy566a^weXA?wC`-ug%I+B@6uPA3f)Z$eDR;B`3w95&`%iYWODR=vb}ud+Q5qDB zODXMUc6YHms2jzp>sE%-q-(n!LGIt_Mlu}gM*RQIZcg|AbPI~w?v!>g`zN!10lQ}u z61@@rOtG&!mF``3RSzoBSv@H90(K{|JC)tp>|V}pg56u#-OKK8*j-#kX_I9n;Voq( z)kEy|EhqZfPnaTVlW$L@A^A7=OaaHHb=iYrx) z$fzV4YT=^Pl_c{zcDJ+p2)jM2imbS}u<9bz{tR}P!wri2LC+K~RSn?*hI$zFGyE&f z7X8IA^fyBHf;t@t0~K^)2{#( zq6|Yg>;t3=0G-R|1W+l?fiD$l;E5ZT;2FVv4+|&{C;9 zhl#$Z`x`ljwUTnk#C{Uxx7DC+jJ_j(C<6%F&T{?~>B#Qf#ksd2W&r4Jj`<4E7)bvx zhvlep6##k+rOy^afF@#$_#u>&Ehed9VhU<^52w2WC;+q<@62b5XJM$iVHLYyScv%= z&>W!G82yD&p7=d;krqjoFOD#mutmA&i$61}L0CD^F+ue()w&*E#VE$N%&2_xIbA2{ zhtQQ4<=#aUOOkU1bLlENGui~S9=F-bIOYx$)|1gA2Gz)Wp^=}03;h=i{W$Ce6NWn} z$l(xj45the|k zr#q8l_7>MM8g3Jp-r`0n5%UHUwv}_=W|NjG#qC_8`w(*s)}429*i#%w;UKXJBrfi-wlfNE?wA!v6!(v1-##cvpG4cr|F0KLYg zeH>whVw8B3dA@{nX8`?yV;SWNnjCvdN z9;34i`YWSR2K|lE#Rh%Ixi0`B$v$RuwL$+tX|u&942A*hVSd8tb_S^*jS>Imn0pa( z4E|Y)%Kl4EH%3Gh)%(F9)!!JAuBi7P1(!#$6W0l0LcEW#e)#577e#F_BXm9X^e-}~ z5U4lSKW8z|J|U893VJMAsdEt)0Xmn%#&XzHagn0FG7D%u_E0Y7utg?p8l#m4&1SSY zL^U>5T*_!W!o~nCX7mWhoF?j+=g*N}8Q#@i%3&{Y*lckvqeBRL6z>CWQq(5?6B1|< z^97dD&_py$7G*xqkT8!dBMr_M52#$k9N~~vyuzR-JtReWnC3u3!&$IQ=fU4i{4IPb z+_Llwp!?Ao_rl$qfz~R1nens?ipMivhx@$@v>@?LCbg9hGRa?<^^xo+{+TsMbrV(D z7eL`JW)q+OG4gMT-3qzn!0XB?xo20vD^_T$d7Z0!;`tA;C~+Ui`Lze zcPZ-XmwBI9{ltrK&%`I-E|62#_08{XOL1;~AGo9Q2f&?>Pid#JJ1d_!%;Pv;XNs2p z&+)er^Dpr~fWzVVrTAuAFugi(rr2F@4&1$Pr>^^~;8HPlU7(OcyA@sqdQWz1*gb>Y zyRwKrl>L>0{O7aZ$tC{;_V>*v|7C@Zfo@_RxZNuj7p@BggHIGv?oY!%b=`~Xz6w{0 zKNRkOtMz{j{xikwB9dW2QEyx8O^RJb65l__Ehd-B+f7U^9vO6b&MJN#obSpaw^ESn zh`ac_*qJT`#&89_>Wh+sp2Aok*)MbRrF(o4++g zGTa7tZRfk--oWmC?CxRr6?Q*_n=69lAIV%f)j2mUR~~RKOq*DWVL|MZlf${F@tp?E z3>RZgzTcol;Zn>4I-{53W>t7axEI>)NJh=#8wd-CPYk*(T!oQ;PIrpg9R6-5gHin6Q75d0BtrX z8d(bTkU_D?LacF|9Ofi18T43m zKdyZrGw3(bmodJ7X3&SxgE)mK>_u`OmHBCJ;yvOjgZif(fu$X0(1f&ia8@wIpexh< zhWW}OgEprf$2x9}K@X>Wiu>g|7~K|rF-^&?VwZ=)vP86aC{vb-*FBVn-t)ePiY1y3 ztRcSn3NEFxhsg6#Pgy1$4^_)@F_h6!`Mb2Kn4#!rUq*WRK*<3hP&+#th(5E1Wjos7 z1X(F|FnT9KF{{KbM${k92GUPUpilbQvX3~#Z_7{1*DU{Fz*n_Ho~(&zfZzKgKIUb-h* zpTk0myU>Wtx-g&YSe75gF+Grl87i^3X>E&&=Nh5}K$z7jFViiwO`3RPsz^@R-vy2y(;E^~pL>!Wkp^9z`3pH&)M+HXnYkZmyFoW2-4yY(5-oBaDU`w2#qz zMpP^N7aKh;%bWgF&;h zEHz8)FsKgb647E%Q&vRb)B-1ds4;Ov)^KsTC}p%>+?$oHE~fxIErG|8`#iCp!#0GU z&+4S+i=7&YSF^gS1>%4~wLlBSaYikI(p@3a+5G61yhxOKXo6fM1~Ae#Yq8kxg$>8v z;V}Z6v4M;YBFKF>N^)r;e7#F|;x4I-V*&(=aa`#jY^ ziSX>pNXs55JrpR;9*wXu98=rmtHd%!??fo()nYxPeUf5cEoQS3YmoziR*43K&I4)` zn+>`cs7X9v&_bYZh$juY8mL(uH0U~@YsCAEZWY_JFH+YEn+;=&d@y^4x=uKZn#JDi z%hX0Ol9ATuCNW>f6qNfWvBn_E{RXkkAj)#r=_i9$Kw#6H^$i4-bnCl-tE3MlFKU zZ5PjIBrl3Jt2=~qF7uR^#x|+D#CS%!L_5SX4^5Ce#6}NoQ+JD<9=b<;TZ|b_T;7p; zV~?o&#B2}k0$OI!?_+z_{bIdA@5L61`^7ec{+rPQ2Ic2GjhK53>YwvVp#27o$axXy zb%QR>IRJFbpjA1qsRzVo9y$b+Jwi)%P0riuJED{kY5uS3K?=arlCdr4AL=17jKlWf zKj&JCos2qzXpZ@?Sg%p|+d1_>+f3N^b56*I#RCTI%~>iQ5xWig70~xYi%EAVXP|sk zXD9Zhf8a;<$(+wqVRkt8gnCRI;&fX@Ecc}Pf!-cRAM2bOupSpf&m%@uNBPzd#dJn3 zVJgFJQO9Usm~z-H)@vjvhuvbQL6pOf#ePP_rMvZnc#DzdvR8bfV=|Y$B6}p|w~x8( z6;%cim!FC$jBXQ6xfRxvqRya8V-?oV#d=1%_dg}JGtw>VDY1u9i=h7Sln9?sIkX7s zE6<2W8Eq9?aA5qLc*%rO|7#KNGa@cytzU}pC`w0Yg0)W!WwbB+=iI5*elf+Mk8&4c zcdpK$&vNGiZPe+);k+gIUov+Xl$+NepBH-!>Y6v(dO^Iz=$dd%-a_$$IKpV3pjprh z;uC{thV+8S9!;`9ioCf%eLb{5z9=SoXs-2=SmvRH*004D57k*Oi`^dDBwrB+J+xlF zDvo=oLH3hIB3Eq z0UZ{fd1$!!qbM7z^P7~nK>ksT_t0GHh-mQ8LhGp5;h{R~Pol*`o8;T#eGjdd?})r{ zF3$$}t{Cc}Rq~ja@1Ya&J+awC4c1@8ZV#=t-WP{F)NFky>GSaQ? zV=>)Ba}l=EL+@CB7uy+;W%$54E_N}xO{_L;y;(5m~L1hBRv{^Dh4p(mMlLNV;HsYNb+wn z#~|t*{}C$|QsOrFfku|u*mPYJbNLUJgh z7NNr?whEi?p$Rf1*D=~E((?=Luzbs;que8MAWt5()M;{}K_qpCTx1YQoh8>XY8IXI zN2{3J%}C3VD-W8OG>YcR_YER0`I7X9K89IBiCrMaUqnc!E0T4LS_H{bB%d*exO9@o z3?eRFWcDOVr)^J(tYbu62HM@^dJmmzcbB^~5?ALB$FBNY2Cd1j2O2+_a~I9|R|9R+ z2wW)ULms+GmdWfX6mwtX`K-})xvVm1L%yYY$}x;Kgm2DYA{;qeBh2K++m56+e2`Ak z{Ij>*V$fKi3i+r(H2iZHvfA2Y&p@O!}(k7b7Y-Hg7$6Bm77i21>UD%4m&2PczSlfJ)7ZeX@MXAX>3cksonP zy<)#u+PK;QE%A!|Vp+;auh^%`VT{On4;Rzq6h>OR(_{mqcfzE>X>y}Mq@`(chem>Q zJJi=(Jm{Cv^SS&|gPCPZ2Iys%uK5i3rvX0Sh*eUHFsFT|n=^l5b+;7m5 zg0pZ7{SYHPaxIg`O}eKGh6k3(?0KmimP?0`j=5ZpXQZWGF6$Vz2pX%F%d+{Du0{T$ zU{s)9ZZqg$!G(ba`4FSq#M=b{t3f{Fp_6KbJZR8I1t->-ZXa{7K9BL5lfUK6|4Wb>8)pE8$v;(qQ))_=QAdPa3L9_$1Ms7BUc0iis z4o14PO>!3_Em@P?&xl&<1lc4HnRGNZu9e3PqOoe7T(?k{fyS|KO7cTXfvmzS0_)`i z9M+6^a$VqBd4Q45?>hOBNk=iSli62LIvuuA_GQ!}C~Tvg=nLB*X$A z*!A*&FYE^SkuU59nY}2L-)7mDk?jCA|FMSiw~ z(_ysO8Ms9je%(di4csal44N74uD&IA81zYDCv}^ApOHQbxn1VfQM#=nP_!#>yWGO4 zIo!GEiNJQbM~8(gik<|rmr_h!E8AtEhbGAFvW}6Sque1sV$>|A7QGm_OO_@m-ECrJ zQNX%e4)oB$z&)~sk#1)@%OqZ zPGkLIq^4luz zEcztylbdHJhyk3nSh56Xj#bPlh{;~ttIUz6cgEDN8Z{7#lJY7TcT z?jC$oj@JllZ3n2%#3Vcad%4~qvh%-}I}9Q_|9iQ|AhPp^BVGmk&X1}|*5_>d%WaHkZE;25ZMn;!4fze$ zJ9584TZ-$&F?obhbNJiP;4v9)A};&nAPb zO@5?N1|2MJ26|K@o?Uz>Ut+XD(0u+wd5n>sU3?_zV+hoC zX+HmVxswr2Y%!ldE}vwi=kx!R2Te?x&;LsfUB~HYpIx1h8x1;Kd_w(OK48$h#n%P@ zBVRJ;qoNZ^sAC45C>k!5!d3wuT`NlEd1x-e26*Tl%TiMq=^cWAs`J8zi;&vvA*_H$W2 zwiZ*z7~O{Z55Z7O+1C)y+i?4oP-laNa@YWaD)ITPm>O@;`5ZRipb3oD7}OUZI*+Ms z2Axru4z$~#o`t!g9CgT`-37r=uKLKJy^QSjT9z85%T*%{a&pr{d1^Kzt?PWX#zPZi zzS_b_>nyGwWpt}Jzf*CjNbUDx4i}x(F%y$^D>|#sT7`AKmU5>YCdkgJFC)#fiyF^p zt4MaL36-if2Ho80tWbB=;!D>iGcvPsj8sni0LzQZo zL3ek$I8?2+7*w1+GgPA<^3Y|WKI$0{Ee!QluY2gqP(O9tLk*!?m3^JguQYzs9 z*E`jV;p&({$2u*L!_{Yu)`w}QbcD*@L^Q4?IR??JYlND@sD)?ZBh*TR$U=`$ zj~YZ4dW6C?K0GagtnhiN)F86LBh>(d$O@mYrW-_7c$8XZ5Lxz7YMVi1*+;28jGDzK zo#p}^G05&*FGef-2FkBl&m_5_uEBXQAVz(h5jQ8P}`p0y{c-8WHK zb2!*#XK12&OCu5QLScnBbC~GHXu3w>ejIb73A=#9J~Lr6Ijm}nPFK%ia}4?hqh~Y< zZ{?UDnXtz=Y|K_IOACjs)JVL|VV{_=e{z^}i_Rgd>w}Pbn1_B4x=2koD8K8~VzQzS z`B6QTbp2UqvRY=)$;@X$Q`Am_?5v$3eCrpF#k&5rhxUm_yJBr`!uH9RyZ$DWLT`5c zLuiJ2hPmiE!q)`^hMiJ+&){0PQrySx_k8a3ZWKBft`rB^uVYdwDOPkRcNM!g{lDhk z4$eww{tt34U*7f0to)b8Am;Px<1QPk@ihvwsiLiPq2mJ@*FYpxp=CaeIp? z$JsqqUZbiiAxMbVI6OJ=q67SXS4-XpbY;rY-hT!{EpZN~y^vjQSVq?MX80IOHlZgejU%+EXUZJJg<)c#8AV+SWBrt`fgsy1zGR zsr-~*a~yJ&ptfp>gY8oJ<7@w$9RDcCO!+PGj*s$J_Ujz~&*%Fg=T5E?|6+g8AwOvn z{|gcPt~-qV+SP65RNDTZ(%)jv|GPZ@r!t%>zP1Cpx1Qgdv@wNUY6Fs6g4;^l*S?>U zitnbntR0{K9f~xgk1mmJ=YGE~(W%N?$5Ls@{Ue2+qSF$^9hJV}OQmD_#}Qq#_py!D zu8xyx^Ozwp2eQCQ+}y@*iEsHRJJ|olR-Nj|l}*nd{2Cy?JBIr8pfz)Z`ID=}yFUN> z>_64m_vKr!F16nxrSu@h7yEVY)hR5;IExi$)A$Pt$i(n-_;uXF_-RCd~{wdK8obilo;Nj7ztky-h$|YR}Z$~myZ9%E`~S#s_{$5Rn2PLiY}s? z!}z7c_cOfBQ6wtx7Do}@D|rBJRrC?KLn6E2j)?pO?nbB!i+Z5fzM(mu-5D(%E>v>6MWv{UN2D?Y$ zE|BlB`!Q2auqzeCjIbMH7yD|UbYr(S+;_0|MCa#&m=Y0};_hgK_itYjMBgNyEB$9^ zlQ>xVAGmLq)`RkK=|H(je9r!~Zba$IZcR5Uyh)7h77kyeuId&G&ro-Dn;BXq>3;TJ z@p!kg@LctDw_P@cR>1!vQbjnI#mIe3c$MnfeL}cdeV%o3c(@$h9cOo-UlzVs(S6K& z72VN%SY6Y7SvbP?H6K`KQ5l^{>$)I`TOoKhW9Fx@L847W1)DQr9CP-_jo0|NtX4v$9`T7 z?(w(qD~f954R(*R`yRWvvkCggYH^RhhflC3KW2I}DE~r7pAVdQ!7L65S?v)JEoB5#PSvH_9atfk%}UN$E3G47^MwqFs? z<97EeqDR?M+-jryLB09zIcW@SgYK`Z-h%F*zhXk^_IYnXx6WS?qsxZleM-7>{)U)T zwgmoJW!;0#l5Q)#A;P&AAxEmUHw4{LDzcWAO^bAcv~#gr^LV$fNBUWN%68fPtmENb z_EE9FY-8kE^;_^c3aLgzw*BGlC?lo4R~47v6S)IYQQ3Qzf5RsKTyYe&d_U9Qv55W{ z;-69eBlM*4<$EJzk=H)>=al~%?&9)o;v%EHl|{!R?}>NH{~eiOQGLB9WKS8L%Pn>> z;)J5_2|6vlSC#ink2FKK4U*iF=n1@G)ib(DT-=jFR~Geye?!kfaQDW}i#E$GJxihy zc}LF+;lIBpr6LV9^Iahc4l@w?XixMG`4rrK@<7jeyi5H@xDvOu=0pd{zrjC9o`AcG zrH#oCdVDupgtX8${EvbI$u=DNc|027H`(r0^LjlO9b{eE>sQfxRj>GeMf=IdUc{g7 z@Ai{);}*TJmlGKz>3;7Z(0_}(!nuba`K7V9qRrNIk+aoU@XSgZE9f5VSV8w+#|pal zI+pLPj^!QNv3yUJTzdNjcUBQ|Xx>70>)74I^!4mEu)B)g6Pm(tR)1)PReVxE;4*2ua?hx6| zHbH)B52a3@w6RtnY}szL9(6{hZ4w_iWPd(&sHU{N3iTd{x}!ewhPbNRqO^y(E*`fk zdM}X=TLtkO(jHa=dOwy%+Wt{mku|aRw8&g}Ui_zN&64itHB0JE&5~}}HA}i(_pqvn z{0gb&MP7zGF7jKX{h-ICqBmQKW=XxNS<)@C-a@yWmAx&sSkgZDVo22>7t4*k3)0E$ znof439Ddy+_J}Si`w49>UX{S2bQBOmxY&ee6oI5 zq(5)^LQ%!i^jF}Y3k{rGQIA$Vqhg^LWL;YEK>A$lAwQfx$f~RODe@Yd^-TI5=p*In zM-l&E`eNu4H)T2P!|E3mMHz$83m(C3Qp#6K>RH42?zgn5#Wu6GX=bYu5&x+;C*wU) zRyi^wVh^hv2S2@`S!92`a#2P%xJxt00yMC@HlvyCPcz$`W=Ym;Ip@AyyjXc$HjBfR zD}u|}+AJ6ER`x<(8}d)6W})@}-<3x)npvLZ%waj7{4E!B;zz9oWtU{bH^FxOJ!7lP ztlAhEWK-$xkhN7OG6vaH3&ZWFyX9sMw-;0uWsbEsRh493WPht_pu87a9cxkivomMF zor|*Xs=62_2z2LvxS$?~w^Y#w?}gP`nR&0;Q?)kpVR@wLn#`^8uT>Z+SY!99JM-^} zG^-O;cVs@!HU79fnR!A!F6qW#Cf;!xWMzs);#SMTSJJCMc|eo}GR1B&8SWl2I}jG1 zh#TR4CT@W%#qL>b7Gp>k3%EQZNx!Avn;-Vv<9Hz&4b)rcR7 z?-&0C|M$e7;QmYmLHUJ9hx?+)f%_X#0{5??58QuUPOX6o2#D#u9^$8FO`T)TD~b?O;E+TZW{&bjw4 z0NTnV|6~TDaPE12=R4o~ocq{5rXQDdA^lhB|B(5ocvs>xeP0}4{bQFipLh?`@>NV9 zlk|5bjmh`jmJmxFH2eAy_>AbopZ zU;kn6&tiYyT-X2k*e(6vh#l$wSFx%7`}-g2|C#VK>MzxIE#|2zG;fy)Qp zKd?CP4+nn1`}DwP2c8*-C4YHfXM807%D`_Ad~4vZ2E4((!GXc|4jzqvFMfFN)ZjgX z4-7U3&kufN@Ug)s20u6W?BFjC{<;+?VNO&h@Qj{z2wbna^ZCn|Y@1rOfYVzMT1s%>T%IH}iJp|7O0Q`BBEp zCbRw7Y<6e%;_Q2~*JiKJ9?IUGUC91y_7}6C%RZa^C)r=g{zmpUvlGcLWPd07d)a@N z{gdopW=*a?m&@(RU6(tUJDi)y-IY6;yDztptLIv|Pv)M?J)irH+`q{ER_=?r-_NB% z$Ey*k8ob+IvMD|vfCx2#x2*Y9l_a9n6yl}SdjsCpyAhG>Aw-uqn-YHUX~Eo% zng1X?iS*y6??U?Z^hu=2%)yv3mt~Fy>GRo_kpERlU&_9W{N|dsa$iRMpXdG%X?h3y z|Mrecu)weG*oSoYPD0c4r-lfBafmhhE*gj%bKON8d-FxCIVP#rZ_fY8MV~y=W;TNKqxec2ACuUP z^iz->58u<-gY+NcYcHPpb$B`t*6aO9-+&}}=HFs(diJ0R8HlJtK9BhP$W((jmYw(Sm1| z@P@5tmSa;$E3s*$5613BTEjb~p7|NP_ldIrys7Eo8#JWW7h^w-^mk+TBYizKhx89( zc*etg1@BV=&zO(&hj>TQGe3$|kY>EIm~ppPL%tyC`#k>m)fL`DNcZ9GMo`b|B0Yll z5IuY@e--^7Lh6|o-ahoq&v}m^{RQu1NI&hZBYo2QIMUC0k0bpY-X=^y)8a^P0>2W_ zxD?V`!LJ0gZV>4QWj}fZ{7PUaI)wBn_?5u-NG?Wt7x7q%jLH&W_M+?02zn{e)gx7rHB9@rH++L$~S8h=keuXL= z``>pejBxCp)ZGFQU!nq4`eD!Bg*Cqm_Uv7t(`|Tz`2!Mt*{YHIC;MypzxH#4j4j$bp=^ZYEInOtc$TAlGmz2mQRdPkx1oZo6! z8g*0T2l=#XX&w&*pD9(l{_JYgH%0B$sj!;8PIPPYeyazPDe6#EyUY=M)UT({XgHhA zb}+Y@D0NC_D*kyl&J-sq?PjAbSa7-zw<_o;Giz2LYj&nn>i8y-N5#TfO90GLt%3qr zUfYCu8U7)E)h~y20it15+Z1agoZkxT%tS@Nm0GI-y2Srf<9zQDKnJ%Ny#p z7(d>qHA@|ejXztd`5OsWoHnzNb1O8(dKqZ@dtw#D_A<&aLU!W8SB z1Bare8?KL*4jl|iGx!QrVH6! zkh1oJI*AQsYcO)cpYJYN+B&_PsFarKjdrK9AofiJH6Zd>2VW?i@6s}0(jq#x{IKrM zZlxT+R%*DB0X1BOQX^Wr;reLl(7~Xj)Ch`7jj*WH2+DzI1~t?=F#$o%25FdW!SF(0 zL$%molb}n%x|Cc2DAM7S59j41{$#bZ+H3L!IY0H9BBqGQ&G?-j zcx<+6J=Lv3f=cbq1U`|wL{~zjD)p|_M=?r%YYV$Q=Znv$dxbZ3F3Zfpk=EK~4%U0& zteX|WE}hLOe+dKt*~R1?t<=k|i&^qJb43};YPG-&2Z9c)CP#t_j&OyxjGyV9baa7E zLxF}JH*}fL!F(P`s;d;FuI*6+(2{Ni4W}s7rBNVEa!PwNUp(4q z)k+=sw{sOr{Z?avuey*6w92Bk9ynuHs@JXiNSCdflt5dzop#i-nXP-F6G*#t8wU#A zZD#9U=me&0-Nu2!F`L=Cmu2VHjh4k1XteCUK&#{Y*3DKNw{8^Onr+CX(WHtX7^z$^ zEFawvmXE5cXmqY>S#Nel#NZ5EZmtq!wZAHl#fVlXh`O!vGkO(p69|ZwE#R`hBDo{h z_)1KL0K3((HdiVp>*Z)s4X}pf(pRGw*ELeIbdnZzbUMljy=_2zakScPFPrgd13QXh zXja`M{N8s2?$K7GW-aJgx6`1ts`}9T$#Vz=UC^w148LqjumX!*6qIX^5r9)>%T zQQH@uh?^7(b*zOCj!)MEI7&qZpDV^u4^|g)qx%!hJNyHJ!gW-&G9YSLrGyR3A#uYx z3==|xHA>{Ltb`8Bme^s%iEfoqQB^n(TZ?HrzO&$xo#f`mt!i?m(sB9ey2u&%*756z zq1xS+4fUt3n|58b?L?ykjKPF(9y^^92(D_Ofml><8JFBiO2WxT6RwaAbOs%Xrqxv( zS;fXqx(g~=94nXUIJ7_)TB=|}cuIH;uAyh8jFllD>RKQ(FO5+Id zu|2c`rZPx(3?$k|R+azAQzBIbBUmk2pK4BaJK_gLC+$wss--K$qj?>Y%)@|s&`MVYVdc04u8DmlTAfZ*cK#< zb~{q$<t4WecNv1<_UM?j%RP>t6(AZgrt2z6L+0j5+COB;G3dXQ#`Ls1Yr7uiRnal&6L zA()gFcD7}=Fjh^LD-6gs4DQ8F8U-^qa{7E7GcG|8t=(s~*rqT{*YOJDDZlM^RP@J7 z(59d?v)t%b%Wi|I8&ZK2EHmJio69js!9fu%+~o%Buo9&)4pFL(^W@3m6Tzhyat~BF zdMJ`hbBjEMFs@bWn)VdKFwC{`;m!(FVJ2HG5YLhoy9%o{bLaK!YOd;^!@fp&>1#VNcldM zgCV>V4K(2k5yVm8OU~jaO>Qy^JqkI5I#*Wdn)2K2(voi+Q)QPT1OzqwQVoly@#RwI z%B58pcUaHn1armqab)j>F@@%qnr*-A%Cys>O=?c8+FP45)~2}@gCtzhJ~P+^lv-nL zHB^(+M@?i;H3v)>Z_mxkSqrvA;-u%m6x4AGYp>m=>oZe%*f&RbtT=_8x;eoGkQ3ay zh{zuI>q`ilNTikNMfxO>%&`vj%9&VXZODn72EfQzIYCabMaHhp9VKwfXpc*IPOAZ- zE|h+6)Y4_Pp#V-n(C8vSX`FGHndQo2$DFRWB>o+tDAH$4Q!I_TVgi|szXDdyR@SKnB807%ss$e6Niz3W*cn||2kywd4e|)3!|x%WR%- zghB#Ka@GgXb=ywj;vH)&TO2GK9)gN4W!D^%r#tkC61bFpok&T%6@H%CzE<~GO|L6S zf#w`0CVHqR$OI5rlJf*NEIiSNtLAA$*r4`%vRbV)+ZEm{ki(x-ezRIykQl}2e%(rm z91dI32j>rR0_H3ePl6>lVI&LF!MGLH)UrBqCPOR{A_mkI$&k`?oq?;1L6MPY)40xI zN7WaZjnG6{A~BLZOyh+H4ywtMHq|{vQS=SLrS(j*(SjMI7>H5RUM(mhojQ>Vt2rYq zp6a^p6M8$0Ei80v-6|tD_GP1l5Ry@&Mfh6pLU61W4xXme0qzB=y}6S`h#UsX*e}R& z?OewhFg?!Il-=RBa2HE=#hSNj{$W4x#>kGIE;=Cy41rSK)8~UBj>PaX$d|uspadA1UfJDOx9#Ka`+MJAZr-zA_Wd^G;#r6r@tK%uU(r;Ss zk-?IPOsGT0nPH^{$KFCLLnLOJkYt+|XRp;{Byfq&L^6^1x)@i&#tgDESMxi|jk0b- zq6Ipf{hUXXrtTVY}q(l=~Vr2%=qycx{GS1R1ddUE=X{9yAYK52=o!Yo~tat zMq$L6N}CQQ1mq@%?GNwAZf<7O%i~?(R;pfb1s7}>2cd9uvKj7pqq*9uEG) zSYO>d23whOcOf#u8oZu+RJ@0^MAI0K55oAK+H-Ose_ppWIQGIV4s75Nu7n$X-`;yL z9qzX+bs3-Q*&D+jmKd_wnqnKPs4Ufq0IO|Op;nv9!dnXx;j>U|>+cNOUMGCqv?%vf z!QjI5RnEcC(@1ip`1E$C1xhqpvMB)?;G(NC^KOdLLI?L1WVSKfIXNd`jvhVWc&Jn2 zozEWj!&Y`+dv(VyauFO-GD5;U_Q=x3OXJ-ZZlHD~7N+K!1-mVR;sU?prB$5UgpAPg zhu|%o#NLUYEDZ0Te zXc}BKHWxU0SuWL=lpoj`>-`HX0d3S&p`@U(l6Z*4c{d0X z9XP}kcO;PVmIIiH7y(YUbiz2=U;{I~c#7)|sUo@{XL2Q-D&!esCAeOm@bfNJrLzj) z=wB&MQw{aJ;Swu0(2m|= zj0k!d;!YxP(lc2ighRmaW&@-kC``9-_))3?V;pFjTcN{HJ>Ia_LqVb0gpY+)+qau5 zudIpV-@kHbKmI#>)3pc8aoj0oIKw^oOm`ldO$zFrKNtkKm*Alhs|4a9Cs&~Hh7mnN zq&i1-Kw<7pk2i3VAfbNr*fQi>#-C(Kd&aFuW6-586X0y~&{=gDJI^_A@!a1UT!VT7`NU z!m04AlF#S>MLexU(`!W)CdY{|jImH~w$Uk7@ia!GUdEd{&>eMpsyhb`9ER@n>XX)O zIR9V(g?OwL#7EY%AM>j)X>I4|g+R`)gR6SvqLz5`%-3%*^YvIbcF%#lAUa@~j#M&Y zt5C3K{L&A`snJ@6|HL^X&xUvrNf8^*n1X_E=pdSC6qN3Aus2m&fkXs8$ev}Yk+g~< zGw7`$qeoNid4tEuuw6EK?xdE$Ec+`Hm8A+kp~(Bva4W!)HV+p82w{aPX>1+(J#lK# zo+EN{qv40C@j)q{(Tk#j4WA3h{Bmf7BWE<^56p`*qptS`U$Z3oMLUhVs?g*R)GTZy zm6!`K=PPBNXtrsi@Dz-x2Bt6=aGHm`K`FeUF$=NvK>|L=YLi$J+j6YaX|kSfi1}0S z83?BZ4VP#A5zN>Ih*GmOUqLiO<`7j|FZtQhl7*_16J?lAp4*v)W!Nd2RMq6NJz-!Z zC-LA0-btO#AX%}?v#nL}*AguU4?!U^g-?T-l$|;s|B-zqo|Mj>H!V}aGyguWX2{2T zNlgT?Emzaufh}yJ)r>!g_X7}_pmo8V#Z%)Cnl|!vGm0{==LAL@(6n|VtTukm`y60Y z@ooX&#rNaqd37XbU%`y#q6*+N(T>2v%`1I4riM1a=qCo}naj2?u7qd51$SWDQpi_) z1aGuFVhZRF^N*`y;sa04R0hW8{YAiQ0v8`wM2=`F;ZJerh?cCh#l9cD3s0EyN%I2I zd8}I@6=X~WFfKe(L7=kdGGMpSlXGxJz^#p;d?DEEQ^Rb;q+rLo^(Yl9SP5H573O`E zN<9$AWEJEZW;-3u6xRQZ*Rb}FdKb})cF67@k7%QZtTnK)4MG)U`310vj9$PW7s*A> zV@ZUFun^bYIqM*TCGdE-WG7+;Frp-*D2Psg2#utsAdvZWI}^y3f`h?qreCiRyCE{L zM@jllLtIVjPW&tzuakITL$5P(9k*p{uB1F=%Tu;|(w0wZc`#H0vu8?JPX;4O0d7uw z3g%%^GZCbQzg~x({!t90MVy@NN*u_kki1kIL&i!3yK{mW9!TGljWU?Z1LR z-;4KU_5#bAi5+-n{d7bd80v^J-hCrgC`>}Vq;|J3rCxvepCUcku~F;k1P&*F0xW~7i(E@=Pp&NBqI3mikc3Ov@Hn(RTO zfR~a$yRRBV=kCPnw1Oj5w_o^V+To5UdOUIK6)qFIGt0j(Xqt!81f-l-cT zc1pSe=AdI-5Y?*S8ZJ~}_pYsX29n~-v(O--8B*8rR(-`*gjfXH^a#{+x5Vr15-&{o zjmsYegd$*U)^%qObTRY7tOEQkjV*19n>UIxNsI*zxw=5QvXlZYybNNQi)qLkLNvw~ zY(edgsQE^DZW6uNPyJqKYMluTjrt6rFnUqbsAhcz45f{t_Z)>`nhfgFvD8+=~arji^r0O2B{Awll~RlSN5j1=(V;2{`-1Huwr zTmec)vejhKltGfPHzep?l43w?Xd2bG>MS$^6$E9W#{q-;3n=rSnHfWSG)&@b3;0Kc zCp@m127>O#tt}LykcD`(6bvd+e6Dgd>J@$(Lau^vZ772eTDa0I*e&7d+AnQAoJ#KF z-Bfx&wSF!_?FC9d&;_!T%dL8=9}Op_0bz+c==8J=2u=JW;a#K|Tl*!a=HaS)x!@4r z>~^_wQjfrv@TZF3KrN#cXKFR@CgM(AJ3yfTIf<&m5!5wAok>y9`;n+3sObnHsY-scoh_RY-^C>7Zdtc_-TTxDA}YZ^@NzKfuOw7Y&dfQ$oR|< z1)^%q6_b&9-D;yTD|$cD>$271Z`3}=>CiPhe?~2JX7dbi(oNF1aEyUh=%RH5aU~xa z6=U}4C@-Vog2OiCQ@&BE<{{-e+PPwezVt(kS?JO?nz7J|Q&SirQ8zZ5Hgnopwx~SswZ3jI zMlmJ|!&tRhy)>ZS^~zo>{!@k6xC{8NmGGPf8PDQK@P9K_TEhl_9%~ddiT-;SyCU~5 zw)TBTjLoF&U5+y>YPadixPMZ|6IJs9j4*|>DP-H)2r?XOVc`^h61IRpAMnU-HHl<0 z<1cQUqlD90T?|Fh(>VT}Y*?eGHewbB7cf8FFU;SjO(gN4sUu@`TMN-i@g${ePgQE5 z0iA*QSd^VV-WbrshGvR06F!xUfdE(R!g8I7Vn~{}xm>^!Vh$C%cq^zh%?gz{yOZf> zpg|*KLXWG9+gc7CxiyS-0zn`ey+j$o4Hp-%b;#^`QkHu)GiuvF;+Ydbdt)fB!l$Tf zP(f{VVRr8xBaF~U(iPxj;y2@1Z4pGMV)0!v zv1_9~Ug=Ehxfcfov}77f?*#><_iZ{;uW{&&-E_*meGfQ{Aj8tWhpxH5gqw~ z3t`Cv5z=H)xa{J7r`+#!b#|637$Z>)8zNK0g*>6%Rnq~!;z05I+{ks`|E*)c`rJ33 z_EVoZVAB8c;rq^9dhi>cOeFJbFUFJkv1GxE4d)Aeg;*@VD_KC2pG*`?ek@(^n9oo4 zp|tj5pJ5tDSeV#{);=VtOf$vLwZ!u2e8EfR5&)A!C;p{T@P?7`@E6BlAO4c~8^B)% ze_6~kRAe&67b+;NwfLWSjA9LFH=&N%by00 z!&uDeJ`?L3+KDtNX#qAIYyhK1c#f4wC`vwe>W*t*wvb zZqhGq8WN%uXc)X>npyAzea={B8f$N_7i?Aoskt^FFm{mgf5f? z>9^4nt9%=ypt#hLvXJ#{DnpqP4C~bo2Clt~ZZAVpad{@5F>#cb!v*}s<(ZNAfa>tt zEBQj29*esouKo+86mc2`6}{vzio>2x{DNTdO4{Ai8VEf+);XL5F~I{04*>zI{MxHP zZS8x)9$XW&(1{om(Aq$<4ss$;AEuYaYV8H`1xiWLI*9H_ATkFNX(TkyE99~Wb(%nj zrbt5woA7BeKY~8wEA$9tSicp#hi`h@?DYQF-S3ZFeZz74S2x5D4k!x zhwk$0$AOKI_q9IQnDsKa65}-idtee=3h-a$#3PVzT=VPqS&&edKv_7y9 zFvGz7}o5BLxBF*W=bG_ey0K9m|+{cFA8n_u$L;o-3 z3n@@w?M>)ADq#QhvIi1HZoLedz)B$)goTQi^aAFQi?kua%$Ho|1vl4QU>ErD43j@b zvi<~#wEhGv88E~Dg9T4Bs3c=m577-$z|NTe-Tf4^Kg+NEM=+GbP+)`S;b-LYyTFuP z#C~#UoyO9GEG0+i%D~b+$Y2dX4qBIjvxmMVVVj7Lsn00lj4O zlEnpx4Ha<(T@z^x>({11Ttt%I5H#5k?~{Zj=JT~QC#qT; zq4k%zBq)9c++D?0P{6Wb38*KpQq;gj*h|Pqf`f@BGji+#1uZA zoOl2zJ^mO$(-j=q}Qj0bE#x*0Ga&y>-qH}jQ0Q~ z^K~Xg&^DGo-Csz;L-D^IslhZjIijVZDR>W15oDc$Op~6#_8GAXwW0MdvGg2;eeFqN z{iKJvG3VNAqE>Wb6dFi>4T#quenbcHBqC351hJtR>0|%(x2Tl3kXwDwz&?ta2B{-` z%D!9yT=IWF+u7z$T`T7&Zj{7=U|tflakfrHDV5P z0eIFKzf9`_a|YK8mB^n?r;_P-JdN2>_=;;h4x*?Rp2jd`HT<B`AcT z?Jy9d+EXoZxk0GF+S^X`C`NA!tKTM1CqSBsEadQDXk?(Qm<-AaE~Wt#bSpA+L)ZXu zpw||<;!3i=thVU?q+@1{z7)Ts3Bg_eAqnwAsAQj)?87DLC6l-e;4kAPGq_~EWELn4 z6~T}q%w2AzkBGoc?p9<8;a1?cn_Pkcyp=epB0btiT0PpI8v=3ZLvt)R^Ydsw4dbI( zNrzyd(e>i|qn8dsLi1yqPXp$o+5DrlsgGuv43TRXN`VrwTEB-F+=JQE^t5bPKhCP- zupG{&q*8s5R^i8RewD5o@&~1ZjZP~79Y;#cS8*F_bUN0=zdw$2<=;( zR&xZwF=jeI+qd?HBiPy-_?)JvR&QsvKh?+BES}4POYu}H9!EEMCGqX!P}{{S3&%w+DGnLbfCUcvL_cwAd+N~r60GO2wn8uBXG z81t_W2|`55e4Y7%WH%*WiS*M(ylc>!s8**+c*b2mo$33d{ER05`+s`?XGr^Pr`r_R zroc7@wkfbpfo%$GQ(&6{{2DqxX#Q?v)_SR-K^bDeP4(<|41f20FlN5S(=Yqq*W!3| z#hk(I#W|#t_|E$bZbtH%^f_El;Nga8>G^8nJ3r#Q{V26l|GB^`ep{e$hg@-(hAYnl zqyJ5y5Cc@+Ham)&R=jUq#0?-mOveANlu@&COO20t@V+)5HsYfc`rnI*n|N{$oWcia zHbdGaefHy_HT`!Gw-rGHJd~5gSU&0!-eAIm5s@e3k7KrZnVekS7oTgTGi3;-V778`pe5v4tlO$Yq1k2EDlU zX$zG%fr-82MR1QFGSSOPNBbxZ_T%>sV)#7--tj2oxSh`=L}jBFhQR~KgPZzqF|35E=YvQ3xXe2pFF>}FZ3=8tV4DKl6xgP~HU+jRuuXw&3T#v0 z?D3kw09|^UOSRNwUV0CHaP@_n(*XM?GED)z#J2)z#Jg^fO-m(W}TJq5%Hhf1l{v_~y@CNk2H*kK(ei|5ir# zMt)NFZDZL_>elW|XKTA%XNQ~Ys%=YVGES~`d#cv$$<(GZwF_6SsqJ#wQ>~??(eaw~ zszpT042xcV_S?v*&oL^iiy1ait${y<`{S89KNsIbalw}>xf#Lo%V$5zf#(nU1vCJG zy>Gu*Xub%(>p2I$37?4+sbrf$CCL3EM>MJc{Wr66je;NsKLi!GahKE zF{}GIW10y_XFE?J=|?VL_HjB9bxdkO9dL-7eZ1pu8Dl2!Z9CmaqIMQC*oipWHtl{c zS5y;Ghugau-@wLQP;_U9f-Gt}75K89N~G>FMh`!zbXpV*H+0UJ^DPDA;zY(28ck0)!PKD^U#q`ZB2&vhv4tpgG%gaK3;vB59OU zD^a6p^W&%b`M-q=$Ayn$s>I?q51$Qqytz=0}m&Tzujix9AsUdGzVcW1Lq*5KWR)ccD7T;U15FZ$6E09r@)Up1%CV~@TZ*u4-*=+ zUAPFw7uZXmbVtU#%pZe(?vUw#+SYahk0GBbfeY-zVxwXE2+~(U0lSHtiB_Om@Qv=+ zwP=l;1UGCZCIQjhpbDHu;6^xL7%f1UiOHaGntZJ3G&2GTybIiwo%k6N_!#7)Ra0tJ zUH2F)zzv(!t%)E))IoDmxIY%d0o!PH3S+^WyCw6+pAXYf708j3!*nCcqS6l*x5cc6 z;21F5nF_pnE(o1z$Z0vo&UU5)iiN7CM(nDP^AP}AI<9IbP6wjos6C;&B5-t(>R#zk zYp9ht0|oe+p}uCSuUYDAw)#2~Ux_(LBH_eY_-Y2ns)_NyM5COCV-ZDGki9(bq8KS- z370G%)!xNtGaqKa{T>N>Y{xgo8-5>_8E9FDwSqII1B0cL!6#ZsWvj_rC9)#Fj;!+~ zKZMTvC|Roq^nq&(_9^mBRstr(wr3J%s(f?@p5k6)khmEP7u%!wHH&tF3aX3qtlp0z zwXzmNSPL+sSRO z)HoL)6^ksf+zwO?yEh_D;6a2E+A7eeMNQCWCG;7Pb<;Hi@Der>X9MWbwJ7JMH@5Cn zrH2VPw?K2TP(`4sBG@!89*768N1L@?xfOQqX4P`-c(8gyMKB(W2VeyMX}J@a=;V$B zoUh~uquz4!W2Q?6s=;mcTT?yILSEpZi=->HB6*PtBpoQ-wt>oGzLZL=MN z6Hior22MlMwS;t+Wj=;A-mFT%qwzcl(8a(ZAd8!rdyhxFxtHC@BVkLB;{Ft9=c7PO zu-%uDH=RuYoXwmF+G-wZyML9EEj}{)F=S&QGXbw@J6i!vFr97qDkrn$W!o)_Qg=wR z6}ba61+t)z@xs>Ck6k4&^TiZo8@Eegud4-k|P)MI4f^Chh70zBZ1U z*Ox{Tb5Yn_VmdqV9ZAe9d@f1&Lm?}tGcMGSDH){`-j1_rAa`e@Mu=mVDJxYu?7 z;VSYU2Z>`#KKog;;e?U5j=-9XLHgOO`pKoSP){iuDhW47!{CF3$!-dnAt!?RMO}Zw zbPV9cA?CYoax=~@w63~cqq=Q6Lr@S&U=_@B;qD$ocg0`Bn^Hpc1EUpn{sEL|zl-xX zsCX9w**$aVAxJdSc567$paa&~q%Z{=qtT*yO>^=%J<%8mAzx;2<(H0&wt51N} z5xY5T1}t7Z^c)KKYql;Q%H>%Wxs?+|$$n#+=3_RuN^lpw7&A8@BRF-GS(S$*_7X6W z0}IdA&{4v8#S=Y1V(J+aZD%*Ku~0Ksq&>(1bun}pu$;XNm0pbLr-9~32#aPf&=Wbc zk2By`F*zTw3=S8>*oi)=#eMN~Vp`StJzUj$hT?ImF)bK3Kjwj9YQ4B~F=}A&fpO3_ z9$8i_+fIN+7{q)3fpL)Qf=n;QKj-YZs9_C^3oQ4jSEfP)z0UtHql3RYE|3*n2LTsB zAon;o53`86e?~6xB#g@G2Us$Aq)-}m-{2ZNxCmDZ0=tNnz_MabEJk5PVk2ce%{Pgl zwRph&D|1)^4gnD-;MT{qe&E?NECK5(;GWKCp>yZ^+2U|UAFo`Q{v4*Y5vwYhu^Gc* zhp#UUg(DKE+!`s26cPbA`lN$gsUndO{c}f z@yPWRK?FDX&>@fya^eVWDlwZQF6% z`6fiIni39-ZFUePL<3`M)i;Xn_Igny%He?fpf5^s!0Z0fFA$}Kt2dN{;k3`b;l20Y z54cA?F)+a|krA6w;*Z!_e)fZT77zKxQJy7a_k&|aC>{zq2ZZ+?E@ErY_EZmhngV+Y zx_w?rP^1jHU-h^It-L)2-GBAb0g*T8E*q_z(>0Z-!}UhsB;#rt5(>k%lwEnsnEG1C zP8@_>Ri!hMYHnNpnJR5#DJ#r7N=X(7KiciEH0=mE~qUoh@w+i zTrh8Oo&YLt4l0Xdr9@z7)f*>;eYP^TvOQ!AyapCHueqxKw98&L5FO>{GZNUjq^qYnSOY)J!lAC+y`MDY5S_Sd04@DP1m( zp)@tevj{%rgSB!iw~TOH$2ljLl|XDXF`$k3soLNIkstBAe9XRzQ_*sP$Qbfyvj|1e z8pj6MdC=)>H`(`rgTfK-Vqbxf727RsZ+<-~RP}~?y!sF+2AYSM^6gxUdR6b49A&B4 z15ufLRC!Cx%p&TI_H!kL(t6Fe!Ex(EoJR!+F*(f;n`Rs$=r+jKkDX8edF*iPe%LNN zc3LF2o$EpMIUKB*{peM|)QEGioc31(QHA638$ePKXzGA{+=y(h3DkICko`xnC^h?- zOs!7|MB&k@srBx)uv3MKI5&gJ>|+5Es1VJ*2zZ~_$E}4Tx2o!))2cVb^n$INUbN-V z(P8mG4~E}hM+fT+GOOrwrmD*>Fq%VWNA}l%mEvReaXx{0D0abTdnTPv*~5fR7k`TE zSj6KoDzNQ7)|0})aO5+vWpBJzp!7`Ex#+@5zyV;piDOJ9kA3|_RkO4TY8)Mj2d;;g zV9#!epJ@(+N{$Y}l-$4*GlKZwxyp%Q8Dmq<`{yukD{-N)ZLza(-S$4Q#R7R{-YGjf zX)eO>PM(X59CR)Ub>SYz!UGBE`tj$}fc=xL^>#7rOSNxOOyd&!&U!$lkC`2Czsfnx&Vc(U=eR8?b^ib` z`!%rHuM}434o$ zPKx6Z(>0$aSh&`9ag;dtiB%aFYf=iFF`?0qHS1lAXO zUxfRi&a}&@d<;bz0P<;ic>{$LUVajV8zryg{GgO&ngxOdC(C375?7+lSXm&H_!TnI zqa`I}fpFqE$rJobP$FbLIwJK7F2~tgw4Dk8&e!Z}&Mrc5gyta;ob)Lh%So|E;Q$7IeKMi$|w~$vD*+%!n z7#JO75*Rk;W1MeR`4H!uRKAq+Eh-;HK7l!F_qQ@4&_5numQxNiCX!mNF&1V&CP?ve zjad@#7wc)>VvYID2VYrCMjLS^l*Vf|SB`|S`f{a+I)A^_TX_$kdgbMMYUPC$jO@o; z!OF`#%s$TRl~=91x6<4f{?@w7x>IXN-ey>F3g`P)kvHEi`;8)h*2fAS$cGg@5XVX$ zh+_pST;4`m_c97%`^(VYQa#`EXVGRKQ}GF7BTg8jmU`Nk_y#oGTw&)Tm?qA>Ft!{_ z$%q{-@t$|ukqH3PxK*HE+EGW*ynEHlWi-gd4)qWT-;=UMX6B*C&k zSmhRLZ-p>z!|L1zvA0`You>V_O&9rn6 zp%)(;R81{cCK(T1FGkN5tEQ?NAw-)Y3){%)@4x@P_{5+*t6na1`%WmK#k3H!DNRKy zW1zLCEwJzfE#YWGTnuu&Rm9v^Y$LO|z!B-PWZBF?vw5v|1{q0p7- z!~=jsEhCxN#_A2K#Imb5hN`&q-%x4`vLax`1HelK9L!^|K!k3}xWJ3c$8^4f8ZCTA z{~$6G@%>%?X1h8a6f3ze7&bOOIF2gkdnm1nH4V2zcIf&6)_JRFgwNxjXM^GK$GWcC z@qFDiP)q+L3^Q7~p7WDc{#wpYQTZD=Kb7-={%QCUUWI;{>kpiN%x55u`3=M=hQ;%q z+hV$+{XM)iuUYwXbN6Me7iOXqcztrqeGKy6pH{J+9L#*XGVu^n900t*i}m-RF55N7 zQ+K7h8kK+o?FnWA3ROA^(vLAGR-aryj7m~p-$!y#Lq9hjWxF*fb?cE7lq%k~dnV8c z3@09zx{Ez7p-OiH;~|k)*tQTK67sYaa#d3}y8?`yMiDv%; z38GlO38`uyjfR?W8R2HY&mF}L7o3Jj9`Wob-h+>!vSz*t#QC8#VTQ^xJlKcl?k`aA zQOKa4D`%gU7V~#1D}q%O_Kbf45$+1u*O!E5UWn?9E~4uILJ z;&9$m?+dx%bG&x#ufDrWMdGI~n-(g7xHs&OD~e(g3{`?mZYe=-xx9 zi+J~aWRSI`UywW&I3$q|&MDz6ZATt_n+eEhJ7oyg)mU0I7F1h!F16SfV_gp52^m&T z6&T36{7XQf<~0nFgyPv!lHX;O8~LhPY+a+4^BgMzlq^TNt^*=DC7fsqk?09(l+VED z+8(}2e8+wfi`&OoDEem8oG1s%N-m649&agJ+{5LqjBBBkqy-W`~aZIQ>J>C^ zCNS6Sn2jqh<9Or{i$vv|yU?M@9YK?Nsq%CF)YQFK^K~~Mgaw}bzh?R0_T+C~phZ8* zynlnFx)mO}o||Ko9R0{M*d~twqpO@_NO5)ii};$T>7PelXJ0^8{IJZcf<3)^*2)jK zQ_O`QbT7d#|J(Nx0)_r7i%$Fg#P@5JRsnwCePHaYae&03;w=6O_Rr?&!M0;+?N;l) zjnuH&2k#NUWs~acZ_)jJh7^u%vcNC&F=K@*=4DE3%Lp>RB%Wu^O>)0><+d3{@BVP7(Y>FS1e#rb9 zZ~|xbp9N_HEkmuIR=Cn~Gtd&szJ%VDn^5=J+>@f1xw?Oz!WHUpzI3>V&gvH=<7&PB z_RR$y?kAaj+-9PR&oUTgN&(tYci^^Req?_K5%5exY}%H2W;?$J4tixj02R>F=8l8T_olG@F+h`(<`rlr~) zxtbj;GYE64pKn%Hge>*QkwxQRr-!x9w-}nwj7QWu#sXu*9dZ5u8tl6rUeCNjXx2d7 z6XBso{H_X{%HrW1ysg2}yp7_Zk`d9E?cC0&z|jR2Q8ONu(JMWAdPU5P$28DbQDVkR zoNZvJG1V2NN-_Tg9G(-ECO!cs?~UNY0WpS&23B{XuhIQ&MyVQa78!FIk8l3@49kAO|K&}g8!%y#dBmIDGdmDuisfGmN0 zzj-cNO`l}{W*<-3BVewMT^$&BG{+#$nnP9=(;*to!my(;X}Cnjy|N-`UyS(>b>2WS zVa8*S&2~;8U!epOqm$u1N^$$Nipb2HKoXB6-eek#v54PVi3~pxXdR0TKYwmr-P9YF zGTcV~Z*G7)*tE-b{>l|YVIjvJiqQqu2Mn8EX*|$;HXeb65|9hV2or6Z6lpGr*dC^- zHv1NHg1ylvJc@DG zaFf89crvIy{vDHru$74e_8WbS9CVK-)a<9dJuUWbn38g3T>Id{=Uksc55#EtvBu?} zbDfuOGvvwBr;sdniH}=!Dml0S_E|`~_*Ar9Ahh|mSQ@2|D&5ve&$bjvu^#)D`t`z| zj8?xOS+^1r0nfZPtnqxLnSd?8?fe7AV1mpks9Qc}0+tBRSS2US*q|_B{(iaaQF8&r zbtjDvOm*<_;er8ug+=MU7RW3}9>)eq(#yVq14_Bz5Bss@K8?C3&h}JrF^Bu<>ru*- z9*28<>RX2P_+Acwf6%=gF8A-{FrcbPUrqmP!0f}!5q|vrLHxMf=f{Ams`(A`cw_6*nw1qCHEVTcSys zYhjOHWn#GEWzns`^=V%Db<~u=M#5K|{GDk_kQl2h2}wZQGCCwLph!6$biWEqYAKIM z09?|9#;*pVB&=`LNmS%u9f(5QOW6#upTM3>;a>qMR;(60ADO6P1l&`M2j!BR1t-W+ z0{5Kv6Gii@=nNu2Fd(&GM{fodFCK_5|fE2E~N zK_QyRTh&vx$?pl)c>1*ZSMc~cc=gvl8a68hd`_P+geW|OMJxWWtz$~lMXY-VPoZJ! z5WR;*hi%|4P!B>`yt;w2k`qAc_YQG2Jz8*9f-?>*a<+rZe&SC9bP0G~%sf>ALSRI# z4q_L?I0e@rOGEi7D^^b_G2I30pon@l5j>uTgwaZMq5OQ{)rE2?4CPOB z@b-(l$*)zZ=3u?=i1gCmzTS31cvb$~E4t~HcDqBLi ze(|M&C_90xo=}?94ahs22c+M%f^Lq$#fj0y-5;T@RiHJLu-WA|W{3q^fPntbzQEIe zs5&tk5|o71938VKLFld^LSj3MCSaB@9w<}|L@YGxyC5Bv#qhB?@qizJ zG&&CBI&3vKvHuj*cx32cC2T@RCJk0{HP=CScQQ9N$0HSkm3*1&L{2VIl7}iU9tUwQ z#uSMTuXIks*MyL^wfvfajfuqa1!q830&bOk?a9v*&k*Z6)$_#ZD9t}l>0+))R@Tne6jkG3pvMe z+Zd0ThC|24k*X(ey&lr9&M#>y?za0$lvXCDfpLj?BA|alLY_$S!x-7u;Bln7y56W? zn=r%h<5N%zZu~arvl-7_kd?Tj=zWEsArQV6vQ$mkZM&cG8=GKPx`$XW-Tj>#JTVaJ z2Al~P&Wb4BJ&md>tfOHDIJLlA%}j@_=}l-eAtM430MVAGaSJA@?w{hR9L^PD&dtC- zryrE5J1!fH$(I}6@+@5!$c#(j%(ZB`~azGHtoElSwr8F|KP)U&H3@4YL|H zBW-TjoRe&hvl+=|INKrF4$iugb&;)?Ts$q?l_&PBH)ndfwx`^|sM6WR=}bD80^Qkujadzup3cq&RLlyKqKfH`#H3C2 zn>TOXJ9%=;=DkxowoJkg-ez;)(Ac+k&R+alZFgs~EtQy**yPh}-dw+>ang=vU(RIn zcBPlLIbEB!Za*4*-s#{P&&*aGsP0{7a+|JgH zPRDT*ZIh;)-q@ORwEO^?qKUTC1|nzoLlsfYroN$RZxb|&K04b!o@&bt>?0J(!S|C%#* z{{MAhTf2L*I}_Pf)vZLju^GnPj9#Bg@O~}0wrGFwYLl%WchZ?eLqoxUYK!gHcGB18 zboaXH9XoTg8emydCr|!}Ov_fs-OJytcw_ddAV?6&do82vq6VI-{XjAj7zW&XdHgB2SGP8AaOVgrcs-rXG zw)E68&F0*e#-_8!>q#mM{fWknB^nY9O^j}8Xk>-l6x1qF08tqtJSk#=}x-PqCcsXv@&gSuzb$6?VQt43(L~Zur?(ey3i+P-Iqyr zVH8-|!n&ckE0xVAccf-@H79eklDU>C&CGNbC;c!0pHBt|cvi6IBJmXP63JZS>|_p; zOn}7lQO$Hkq?^_?6q|mT z#=e{iQVOkKm&v8vu5>1uOSRX+fV)ze91oEvLVKzs+0&VurODNBs?iWKb`X7DGX;9q zlU3Py8jDUtLvyaVhdq_(ryfE(J!rc~9r;dix$cCv`Ro7#b z#0n|cjv_3UK*45;uvkI`>lR_L@(MOrgzagV)dQQ&d+q7kYv<;Ol`qZYv;{O{Vd4JQ z`DlraOLMgk{2KyV(?n!56q^#;i@3)sG(o9mm z93DxFM}pO4pa`nHQ?=ts+8^CLrzulB!FFSUsfkgzaQchE9<18Xm-f@_*Tv&vqC+gj z7sKX4J3i1;2&7n6Sx~%nLq$@B?i+rmkXf>@erF+&Ed);H+>wN*g=H4FG&rw9)NvP`mn4UQ-w@3?hq=+gVuf9WyqoOMeVqU?)+ZySQL`JK*o zCdLOX8SBU18jDx}-ZrN3tqN}a^Z3J2o;-`61w;UUED!d_^7||N{0aMEdCzMTzPs>; zLv0G+kL!EioX74}_s9m%Iza8XV;RL+_Acz< z(#NbVz_$dd12(-Mcrg&AH9@AoHptv2*i58P@66pmKtW$7m!BjwqZ>9?67}B{tv^Lb4eAaQzbo5(soHNk@Q+g@0RqZ zlD;OXS7SAG*JHSkuOe^L>aj1vqP9qy9?RPJ>{!8j_l_Hd%jW>q;_3Vpf$jj82>8#0AmwzQhUU=CX!)mrh=$;I zI~jUOLyHA^Q$y#F$YOHqL-@C)!DWFtmqw%AWdeQFOq&rvYoxskQC16Roj{)gGz!or zGV03dHglv=2WX2x4}fO`{cRKICjwQ%W3eV)63XGYOTy55fFigPhNskkVivbIhCWHR z5K*mlHu^9IUXNR72E=8b6==3VV{wYiWy=H_PyZ;;8i6L!UBo=MSaYG<>2#lz?XpGbc?`ax;$bb4GUuNLSG`kAzTEw~VVW$!7WyailF0eYHPXZN6N7@%jQ-hWDaGw4@R zc3jHl;Qc`UeY9YJ`*#-oUZ67owczcM6C%rEKy~=F(zgZrSb+Pth~Ag>I;GwU8fLJT zE()+ste}ww^SlbsZGiAZ4P`e03gb5%TEXki04$2`4b&k0L0}Zp9|!7?4h@b+T7&fb z{Z)2Wrv_bQuQI$^L&EofdtJB=r6 zo?EP=e~H~{*3tbjZv8JPJ)P=GIBk{mOr%q2L&<%h=_swioi4@m=cNmgKUR7U(m$25 z%pIlYS#@-vtOh)prj9Nz=LBkDC4%iC;1~~mja_CdA6($lrNQC4vebH zoa=~NsT2O(`b&Q6Oh1JdmyZm1{XJ6tG&J{I`Ljr0mh{i%&tXY^r=0b}I#hZ?>+u_d zhUmedpT$}7-$=@md%aVAWcdw|-=MJ-tV5RB)8xH@zktt6WsGNe)aV%GRL;f4wTmn3 zXjLsH1zlP*CYV6!UV$zPj}J~l2=Q4}7M>jZ2%v{FG%q*{(7OU1rY*s_fNrlNd=67P zAd8+>kg+Sc7!kya8tN734Gn!lpg_IUGp-Z}Z~ilv$j!lXQI9X{GjxYQXKLu4;7XLO z73g=y3&AcxmkV?m`mh-O^+rV*eif89-KC*71v*?r`HLdxxDWja^}^%0b>?{jK9%27 zP|(|$7fS?Uof$@yw)tg$LTq)B51oKdz0rr>hEIL4pk6sW?L!Hy6Yna@@F(rHMg^72 zN^n{D7JDP0gob`&`v$Gg$bT9miGS@A?a?@t z;hAKUf~KP0SgH}|8d@6pwNXpWKJ=ndN8c9c5UoeqIQpxq2gw%WJ@4{n=5h^vGV*6* zJgpVz5M2rAG|Fk{#>jsg6R4aYLeU{u_WMSHW@zY0BxpiN>;<^&)kvAyL>CEkKw4;_ zFRHSdw<6W1R08EAL5hwng2tH~@AHStn$0P6m()|UV;UXyp%XNXUi6{2v3>=lsZ;0DJAa|WHMp%Or|s9m5#R2BW0c_!T^&}EcB*&KS;uXlpZrV_qNN{>)U^aNqG z6G+K2k0uLr4V@lsGw0DNfmCl7&`zH+fwk=-fewe+mKM@g8e%J0NZ(eFSiwR%Hig?e zMDwC)a}iCR&d>omH<~k-(6-YVQnqv+zJz7rT8C>sqV;9C@1&!Cxwb60rDw-?MW#K=d-fG&Qp*K-)HFav}eUxF3r=cOS z6M(K&5RHv}gx1p6G}IKk)LctPH08qBrvbgFp;fVi##$;t(8lv*z)W(Ic^0fF9J)ld-Rv>!?Qd*O$=?fG(gJ3KIKV zPs@Dh1m0uasi5#Xv6Zxe_G>6qastp71$y3KEp4RR1^S&)QF4#Di5?V4S-~cHTA*vf z+e*HJvX=$AI=rjoVL<%jPefOTdj+c1(3Jws(9lf+trF-O`bNnQQE#UYJ#KEMOEvUO zlzohD^r6Sit@LdTeW&C}GfBS?=#b36?Nl>|TfYXkik~;P({v5lrLUUpv`U~u;fm7N z%?@f8NcE_LE8+7!boGGohGIXy% zzcZLihL+&MGSTl0=F&}H(-3pX(T20R>;QeHw8843PJs>qnq=*!%YEn!YY*Kl5L?J9 zYcD+^&}C@vQnQziX^8Emm;S0Dw&Y$anaeF)7XF{o_0~SB)ljr-6QG3}sxI4R_0a|m zHI}6SU8JGeWe1Im=qe2@E8AsVM7IgVvV6|!r{nVoA7!nV=>N&|vheF=D{+^ua=w(2 zIpiAa5}K}{aQTpJfR<>8?fMehtf8?(4jPwGw}v>jxr8oN5Va1u5%q4=(EK5{0(wJ3 zvrF%=_S5(Us=ZaE_gbHzg&JaAUryT;6yAguuAr*~IzU}R4qI2y{)MXEz9B!cK0}E` z3>~5`4SCx7EG^T}%|o8EuB593;=aFPeV(2ZNcH^?{aHh-&qEYgEIdW?hp0wFnbOx$ z)~q4c?jf41An;6})wKd04s$H^1tq@m#`yOFNd5Vlx>o9JGF4p5@}1l>X} z3v`$`a=ewAv0+sHnxVNqbOO+31&Igy3gtA!7WoyrTtht4x6zFnI-`7-bsOC!kn%lW zrC$hi4Xr638~A4mEa&zZY6#p;Gc=SepBnfVS}Ty6#dlJtK*|@~NtbHMo#nFwchZeM zG&k@yx=TaZ^0|R;&=G+Ym%F$zd>#p2QvMO_D_@o}wL0BJ?+T<=r@QIF6-;@6zFfXI za5v3e$q-ZiiSDM&K6HZap^JRzZTcqND3DswzeV>7bPbvDV}bkW2@OT#F9#l=7X@O; zUJrbSN>*_Tzl#iwA2hy4qcqeKe=G1kTBxCU@q5hg(FP5z63U$dT}Hd&?}PGd3Ifl? z^nH5BhfV-G=0k7OLxim+K1$9XP>n!J6NhQC58V>{A>{=61pQZhF+EC`tFmx#=t1LA zx=}-;hVHT+rMm<=5S})4B|S#Z3G|4uVCbE}$0)Fx+hgdi;E(7w4b3k7x8RTI@LDcA zMB9h{d+-SgtYhe~apBM>f{Na(C33s(ed?6 z`G~<|`*W(tJkz&LDW%U+rOdPHN<}R7#-7; z?5~f}Up2%r(J?C7tXk(e{XErbi0Ab4G+jZ|TTx=aKs$BW6&0U0U!aQwIuO3L;-K*Y z-KL?tDy~A=6B_!Vl)a&$XQT|rm-t*mb1MIgdUG|jqH?7DTiW15_4bQ&m4>!dPPBhV zxB1W%`}ef=W6VX(_gAP>plfJH0`A9#(P6X^NK&ngcZ$LY90m&p$EI8l=D zQ9I1zRHGr@VIHT+8sZ)1aay7w-eDf6Z5rYo=5gAqA>Lsgr>iu?JIv#ByFiD+Z&sdX zAEz6)OM7D1$LTH~O3-mSEYM+Df&M^&Hl{pGyaN4!jnfj5@&5;n;8MnzbfO@i#}~?MQeb4h7keeI(#w!HN#zwEg(=McMd%ADPet>=df-x% zMvr1i3iArXsVR6>>ZSVye^6=-)Qm~j0dI*O)JUC-G3dI{+~Pk3DE$=n_v|rVzr5ZljIx}CC^c!R(5#gF1(N3;nWQN5d5iX-G(h`><};G27V|V- zf4S~p^o16y@`^)YPYd(k1Rs+gmbR2VsJ0#z{)H0eF#(Y?SR^NxdOcFLO2#r~7*c~~ zNm^(>+zQ^+sO9w2NKHC4AaBuigJJ^ol>$7|SSMi&`nu4l(Jbt5p$zv54W}l3-_ILb zW|rKf9}8aPk4Rog{`YtQ9!Hbv>bQ?8uSO(~x3q^z2u-0~o$OCkyJ~cn3%%;sXC!|! zQj`8k@(S~^V7PUQUK2diU|!VcDf1R$$_c7P_oLLHLcJBv9pz^V^;wwTJ&x-ZV)7Q{ z+3kLzVHrG0t`v-tS$RU$y6Ug84Q0b8=h3$nev9Mmo1=DC4rI*>mC4V8Uul|xv_yB8~5k#|7;)+zzRh}_4h$a?y3o9G_)3b1CL_iT z!w(t}?SA%ne;<-ZWw{(t8E-L!=MT&qpi^MT}QQtPXLnHzIv^M0e!qfVE9njyg*(iukzWFSAxiHyIa+@}@UPIZntMX8Q2)sLL$A}#BOgU+ z>yV!z|48ha(39rlBM%z$=-H9)gx;dxj@)KFX_`XQOm_+4w;YHZ<+Uxx-`sIdVBZ*dJHr(&0mcAk(4f` zx6I#+;`Gm>P9S}E)F$fz3Xfii{IJoBLHT;k&mhmcBc2YwWsV(vf;NKZFM(+qoke~M zFov~o^z$e^5A<>RDC$Nmw){9!J2Fosj2rFMRgt)H+vxRH95%3+hD*NAdTMlCq|rKe z)IDaS@k&iYgk_rq%=4oU8dIz{!Lw1$D&u60xyP(Cc9k47>WqppoL(vUn~=_w7Ux+d z(F2iZWu%zTXCu8LPaRs^0RBel)saTSF1;zT)an>>N8~M_OvoY8=~+{OQql;1`bl zc=SR#Z|n)W5ZqwNR{z+8(F0a{^#=1o>!z`nN3THs1Ravn>#WB?dBEx)xxu_eG;@o= zXWbE*-I$eQSJExU^Pss!=x?#!8@own@s-gF&5^Z7qIX;8);<$`0HwcH*5b;_LCA1r?D%H5%o_Y9beDVPO6_BIAqMK z|0OWnhcDJ2G~z-T4=fx#qT~wW zhrk$tcS?SW{*DQ5vY6-O5+m@G&_4$btcRH;!vn|b&#^8v->+XfO}UL z;8{G6MvnVI=>taVxRvy}fl_KjE6|297y0L{72}>L-6%GHz_@PQ@G`b5o+GpB$CN!l zw}R#Yx^LWT_UrUG@>B3E?;>Mfz#PA%>~(m{HDyZ!RpUQec7EWrny#{qfpf>dA7KBm z$vS`;bbn}LV8!@*jE#ZL@mZ9%SMM#08$5%q6YWK0b-07RGycxtbz((jG>UfPS!gXh zZyDCcN06S0yJBTHQ&<)#qc^B5*oqrvTZXjarbj>0EZu;#f}SgD#ftg@(sA@M(k42A zbO8lXdLEsHbdA*7L?0jW3G>cCY2>#^yCPQL&cNi@g5aG2qwKcWdBIOb0wtUt8!|h% z8y8VHt(A1Hq}wEYXv|;C-javL)Z1%Hjv@bhr1ka>OK5Dpy{?qT{>AJm{k%O2E8(5? zczVcw$@)E>-&clk_rkiz7$0g1O%B!2w$QH7?$9OHCqp-eZVTNW`g-WQp&x|)5a=xM!pq!Ht>VUqmd^f&qR(zo(sGbc`Fi)mPIeO8lxYHu8D4qy3yX~#nCTD zAB;X6eI)wh=4sRV;97B#JXZ`Y_7dOc4_QOv3p|o#oma;O3F)y zmDHCsm7G;_iFuCoV99@!zFhi;(!Z4ccj>#OWn~p*4Q12IW|f^)wyOLGKF3&^4T>`nF`b3d`6yY4abAR|iI49d4B}cO{lw%WvctiTA^nda9}heu z>2tv}@&TLE5=p-iy38QDFZ3CtkB6>8`t#72kp4P!J<^v#HzEB)=r*KphVDSB_`ehS z2J+tuGoObgJrZWji{XcnDokPN_oGaiiE)~h^pe;$Cef#2T?I{{n*NHQSzIY%=^qF`R={16RdMM+6KQ!O#pO|rbFiBpi zr~1fy}|N6`@q+?;3Ce^|&u{VQdny}N+NXMbX#1sBHq^AKg zVVS2PZKejKEwEq{*4u=1G9VMbZ_RgSE`TRBX+55?8?=r1{>)aS2JXzBfqa{!DcrKd zog}0tet~Q@(vQ;|q`UCk+@uUVyNO?}T7a|%bHIS#T!j2S{3?ZsUj$i-^dfjZllt)- z+QdEFl}JB@nS#?O_(GGuMC*`V11Svp2c#x`F=!+5*C93OdcI{xHy}0fJM6e)M_)#2 z(oK-mq?>U-Xwt2a)Wj2&6w=#h2hy)XQj_k4j3(WU+jl0OIXFnaNf#o$7v9>WZ$Vy@ z?x#ISe~2j7!1I4jAA{5;PVG583aL%{4W7N3INd#f^m)96Z_=x{r)S{I^OMLQM{jV` zi0|sr{~(3A7@t9E7@q^ilr&&`9vDl~Aa)%F*^-8htAP(m8Z*8GdwYJ3@Kg>e(oO5+x!wZ>PFjx)ZBbiDD;NKZ5FKsv#=6KR9-b)*U7Uy(K% zcO#u>d=qJt@hzmy#{KYlKcG(|J&c`SHT@B3Eq%e@UEsUMr{U2SoB02BBPSv=;J5?( zF@0gF7uE7Nf70Apd|$4w<6xgG_(EJ1w*%Ctu;vYe&~4FlisvKad3THk@C<#q)85mW zI*asm_ty5#PFmBO&850p*Qd4@WLwuQrB!(Mj?T?GnbqBGDz7fdw`!bJ>B_b_ZfDx7 zODpA70guxHr?V5+?D6JvmXcrcc>ZJy606pr#dv{JMhk~5b=5*4$ z9NsP8j#mf0DoawG-FakTPv`ffJKND@Ar7|H>10yL?VUb@mD{s;9X^NG#RqT}xp{n( zZaSICDT(Ltn{%{Osby(bcPFjIOW6gWuS2hSRfajDb?XVf7H_}j^`xlsGUVq2cMx>T zli6MVNP0XL?M=1y**XJDXkVXaH> zvNpU(&UHFjgsx7RgXuPnqpdqr$#&Yh-D&ToR+&{$_g1f`1$ouHARHI3)vC^BY0+*d zY@WL#ODi)h2ELX#U^%b7eNC>nGqq-CDwShs2^xZ~k>j>eiL;9xM`n+bkeEHwNoGFF z#F(||9L(C2X-!e{LM@bvO8Zl(cV4QOU999U;MX?=^00Z#R{b{Ca`m<~rI`+gFqKkCymq`pdbl?yWA5d*wsE(CV=M z1%8|>U@9s=Ys|Co)>Zh~jka`mvXhqPvs`0c7RCVghh+r5Tc(9@S_-HqD~`-lQbEUk zg%se#GI$oNZjr~$mr0r`;JXmYTZ{3etynmCvZp)JsrJD!g)?hurX6MrN{r%q%wM!A zU*g?Gc#$}hW7nzR&NON)#*3w5dR|jfwj`NdHn4eC%t^>XrVEN04#wl8`dizbw)1;1 z&$7O_oz4!GSF>7rp(b9T$QsFVaYxh5S7_n247x zAoo^0sixOC&huJV`#a4Olx9997Zi0QO5)Mp# zD-e73q4h~O!>--45ecK#$d6$_K=sWE)+o*jiI}4g!GN|q*;Fx7vlbbpj+#kAVe>XBH#@PkCP8RVG+{D&yFB%mGvo)O+ZmRD*2CFt7|or67x*DqD9qG)Mc`j}?~+66t?oYUopxN5>JPP^HGB{JBU*TP)sdwH_k8+O@5 zWe_4{Qy1{A1KX5>A}kMC{D2jVabYUey#PN|gxyJj%morI%}ZC@7>AqU`S6x_+b&*I zP|N9Y+fr(gV3S=76|u!-^&*BkIvVCslqKiyh3Vb=RlrT0dO^!u_oZA%@j%BM)!U`w z@~{%JPhNpvFVZ`~MP1#wUY%!!@{;HUV@p>OyY$s6GDfKmT{J>3gKKxfEw{t#bN)ErdfyhA3d4q0X3 zBs{=5ME+og)DnszQsFR+Dx1=ysv-`*Xk|vZbcsEb1r&w>tqA)N$#b+p*pI_+d*Lu! zN5LE(MCL+yl@YO3dP}at=txGJ;=Jwnt)2phBUUUPu5cnM75To3RpV`4xyZ|7_6+bF znAjXm_)^Ol_>dn*45+2>gCf1j&xbmS%;zSed!qPQY<`p3lUSXEDCX~|Q%0Prtf`MmthsdyCBA6OUTt87X9WO@gFW{X`3JI(H7Mq3o0jq(8mzSwZ({9zgI znXnZErI%d-=kFp4-=&cFUa)M9I0QbM@)sVDskalC^})f$RUbs6#s%qYn%5O?@q@?7 zrng~P^{hCV>XZcwpjGt#?zF&WMQ zTLOQOF}OH4P^V5wOnUrgrO>NYGy{ZeWA@?^iMG9S@WU>Ac%_b2v_ky|IUkBDP4g)W zFNPRd%DWOIk`GCQQK8M=8#-+ zyE;!<0~f#>*#fw5yDeSh?jq9l1FB{61DLEe#I1yL3oOL?EQ}}S%AQ;|T<;!ro}e|7 z*KM|SO?RrzJKN(GNY1VD1S;^7^+P$rrFfQs_^Ca$cV!2b`|Vk!E)`Lwa(wu~hqn6Q zW+Qe1!eTkZR=a_PI0$CRmLPmmMd=J5OfwGWIt~_gr+e!;2ud)U*Y3oOUn~lMspM1`f$R;nv(v|tV)~k0c1>;dCuz3nS2~>P0!pLclwpu>;DH8sC1Y&Y*BtZew zuW>LHN*C=Qv0*@7O|WmEBROS8~@C;v?6n&fWSxO9BaHyZ7E;%vAv6%c2E#W+_v!@;MU;`)nLs|v< zLj@j&ygqcZ9t}q+`EAtF;#syFem3RGj9ua6*7S69xRNS(s*>XaF`jl}oZIyS4DHO> zLezTg*$c0*8iTzmhrWwDlQ>~kt4@Kr%wy|Lw(*LqA`EqMyjp(qS1hmF+w7})x(b~AX!yj?h+-;=@yTusciZn}$lFnp|Hc8xl!WKd`2OOfZtKu49*OEm#ifQf;1 zb9#p(71ijd!WHt<$07qyE*UZ*VxRBT7xyar78p&JgQqYo3p%(v1#?=omq%C8BFNV* zt<}Fbti7||!T6e9$3J80L;0s&>hVsNa(s5So6qP)Hre`9n;ai%V_CWgzlnzE0m>vV z&kq4%xVnU)<&bs-^FmC{+f5-?*%W2qJz+y^t9#No=iw)2(wvMJmRCNRVMpCArwPf< zyufNiaRQO0U8&wIA&}|HDuJ_veI}-?JTH=`yEUh!H3u7%< z*|9pA*-3};z zEZ$E*^?z#9}S52#Js{o8@3~CuhSiPJcm({lLK9% z*A9tV7G>J-xB$@yx4Lfa;u&o3sIKjuzt%du^;NUg8#We8#{nt;AowMAFS+Cn5kgE| zauUEuTk0eiQHy`{3O%z^k}2QC&rNZlk?So`1J+?;E^1QdHJf*ttW(`D?t*wNoU(b{ zf@XLFql`}EVJq%Tx35H)KH%6*ub94x`LRpkQ`A-jr?4ZA)_4oe?MYCGF;LHZl%lkw zOS5={ttfx7)5)(#42hFk=Ip?2hR)7j@x^Ky;F+Gvh%PWa+bJbi2QaF-W#7H(p<;e( zac-GWE4~^zfN*(9Y;SlfVHW792@^uL;8Lh07JjP{4!L|ZA@ZwJCwa=LjsaG6CVNr1 zW>>mf`I&rsdC$r1u|=u_1La+1Sm4E6iR&m`wEt%xx7N`k$LB0+O<0plb;E(syo@|> zA!Qa`uq9&&ZOFIYNr+5d9%Tb|!%~c9@|!1((fb;nGR0=T5(@-=gnmzc)Glx^6iI)l zk<2)m-Yz_-7crEvi8X7xP=mz3g11$Gi3Q8*sOlZ@G50`0n^JNr$X5&9vl!u$@iLbXq;k_&e{yg*5)&PB{6%OJLrxUR;MlG!` z=}4@{+u#+>8q6EiC@7S=3sbxWNM*3q5STq4o{hr8;)$(~Q7dmdpIgdNJ9{#jrdW;j zHJp#bgj+wJ<$Fy-vbeM6;>KJWH`wr+=>DIai3@xSaYb$cE=_I34`MFDb*v@GFQ;Y5 zt-@8g`N%Crc`YvfwcrxpB)Pt~KynK~xv;3lbX?(@j7xsCC~*ZVzO--F9>d%$52E(o@w1~wMJuf-3;OcKg2)Sr%gC(5w4pbM?I zI+(*>J+8(Y5$47;gj@=(;TmjYJ1E<+i=-G!kwYmu9${I!B$q>3H|KiSqKy=!Iv3Xj zS3<%Tw7W`r?gD=yuJ0i_1&3NF&jHj4zP0F;Be?BSmrJ_9k6X#%k15)vW-VX`5bLBC zB`*GP4_nDNXECs8sl)V&E)BZfpn&@poRTdEO4bT$CI}ti=UbvKom#f>i|?sl|oSt>BlzAI?niF9C^C z;M{>rvbAWH`%;Umt+lu^Yor=*Wp);>;zj`%|MP0K7S&PpKbT%gz&6FLOvdXCuo7!4 z?BA1-VtJ`3$Z}DT<)B8k`JA-Kn#;?;JUtyHSn^1N^anqbhnndB!5@0ei%C5e+z1<; zXSfV^Lyhb%y3o+yRzq>OJie`>mA&_)A{ zs~u@AhHT&=aLrYr0go9P~?f=%M&RiPH$Y{Ne`BKBk6 zjAMVYL+A%vr&wM2<-JwBVSSNO3a93&eG?l8`_-g)Ks7}^Nb$uK1GV9Kv;0KO54Wm0 z*r4QTydm-Uc;9(`1M3gY_0%R>+~4A$}8&s;k z^~#JNawBUB{-$CrQziL$q!zMs@7c*J`YDJ}CgUxe4(V+hdh>tUJG$5ZR7JCo*iqkR#e+BFeHz)uLS@yDFT*sf9L3 z1SBW|BoBTlU<1iRP#CBmgu)L25}-!mhoDGNqc25)pv^-b^3wGGyN5^fvAfg`C8r5M zQJfjh%suyf-*eABGviknA=LtSCkvg85OjH+4{c$8MmFej+%!Ck3}9xVUq_oV(-C@`o z4j$dUAXl8y!8Lvu988zZui*CY%|JkBHNsbSDLRb*c87Q1hUZ-v`Jc`?8ow7`P55|7 zDrEOh3mB%DHOC^WQZmiRTrrEWLT`p-A-|IA$6beG2kWzeJuG2dwI~Aev-tn1;tG1i zyc{DNd9!DVINBY>ku=XO$E&w0xTsBR;KW#UQkWpDEVT~D_pU5Q6tog~baOM$fm;O+ zs=eKQsmEX~drKluPgB?9OSLKopb5)_Lfyn2>tBw@WP^ z(PU>%<^knqavVJ;BQ%H4Uv4SHJT}pNJe)hDIL0QRz3+NdR~dj`QlDpJIsI@0KCa`m z=&)$a+}iHT{Q*C#o&6v=emq%BNOD{p@d1W&40z@wi0X*iikj08vPFR@Mh1;((^LR&b3; z;Sp_}ZnKP{r$TQGo4oDjabt458h(50Sk@n9V`bCSR1WiD&^;;cr})!vwvE$pC3zIv zWy21aihCMp%yNIdw!JCLbEmt#ov|1^521|LxLSXe)xKAH>Y_`^`Tt@15_FHGQ*R&f zyYEQ|t&l5o=#}6@ER90ii8L5#EaP8)t8zX+BWt`ewA3!(nU7*Veqs^WVOGlSTfCoZ z@Bg|rKaDu1LZne%-#6m-vsCxi?K}^bmHx~|N7)gEyhSnDI42o1KH7-5Oa8)qX*O{P z|4sS?-;7=Rq~XgNQ6{5%)Uuz?Du>xoL35w!NnFEfy^(MbKjU}|s*OIDGAj4h$SX_5 z$ImRRH2b3RDDlti3R_|ZZM-n`X&l*>?7~f2GQj1=r@a5Z(VRr%ugy`3UQ&Ty%d)bCs;hUOLk2Z zNh+MUk>okC*B|SL2|wqo&6M7K&vMg4(i)b6@?NL6t~xi7QaW`unp*3^vpC;v-$^#2S_LWiWdQ!?P+(GL)IwMP{bgeZ~lP zLD5H9=cG+H7151FgSV+fbaQWkACPcR!;l&J%W8R>PXT1dL9{%Zo!IC&#r%LYn6Gsh zLsJI(`QCmqZCzxF!6}}-5)@dN##siVm$;Zm_B!vjXPF@X3qg_v$|@QBS`R{&*QN^D zGgoSzm5jB{pXt?eaoo#mO16b-1PiqI3g*1--FefPDV%YyTY)`}hZ`sR%!Bjy>=ty> zl3uZR7PGtwq*`QhUzHI6A*wwnKc)A*j6S6Oel2)M?Y~Kf=vS0dVA^eK!M;+zq@n4w zer)C0R&{aXH<86D{bY*`Kr&B;rO|0OBf@D%Spsdzwa|m}pDdzf zpvZ+-JERTYZN6r0ZF$OCGrF<-ZKDL3fHj2Y@rHQoi5|h`_BCw4A-g~ok9(ZwaeBP7 zc!q`bgKA|9BKkVxExqpeqOd-rel16(yL!N3K26%)?ul=NYRhguCcID!Rsf^+8;7Jy zy)GfwbeN^G!`y?5#X@d~elDQ9m|+9gARDb9jDV4%+o73u|R08L7ij<;Rem&5&r2!-3}0;`Y6lxfI=sB4vl2 z)Xt$8gZrgim=?(9uUjUIeZ1*tYc3g!fY`JbG&$eG!9IBh&#ck-I)tK%7b#R30DkY(Y|_- z)ooc;1%&3g_XdG12_kE&Pm{GOSz|bno^8+ChR%PS)yb8B3pz8UfZFPi>?k^%9iTiU zDDFM7^uZHNv$`gmq+bq;zx7YOW0-ybO>%B@HYR(1wj3mnUXWZW`$LkLLG86dSH#Ny zepp^}F^n>lvh|80+;Kp@zJMmJz*Y2%Db}Cwd9Sam8*%wvzITy0>*1tDsyHWRat!vY zc(4L7B|P()|Nd)3-a~QkJw6|liEzXFep1W0gGtL_QY2!5^h#ox?4H8JA_rgjD^GOw|9O)Zx-rwm}?vCa(v83 z!TtL6^Eik!vyi9!%hC_L>EL?Nossc=iu-(K#5|}~q4l?z_2(bn!KzsX~5iYAriT;Aa}rf8cpiQnF}(A=lQkeR_lZ@>luGgF3t;;U@XK zbCPnl*FpW+)ySZKxR%BF!{EMqa?feIU+T65E6P3?c;@!;8HE#!t7PQT0Hpsqhg(H) z50%TB=UQqDowE%4Qt*Zz1-P#r#3H;0r;nK&F`I>`rb*>#%m-9gPiG!0awXS-R`*#( zW!}DQOew=bW|b?Y;*oOpUz8LhQyR4y4P|}1!}PsPV~g3X?)dOZ*(!(EZ;xHTZzg`8 z7VYKPv;O63`%I@`*e*2YCBD8#E#}htQC2)(MuWVq>!o3opTJ zeNgw>8}kBdHJ52adFzZKAFE!nxkGkF!D_2j907Sz5J=%O`ygFHe8=+zL`_&*o?f^3gD$VV%j$~N19%3rEZ54-jIh~d z=`M56-O{(gqAu5tjg5WldB*-eE0y(!;Va8Y3_fj4Yw-I}!W^3i9`Rdg)ni$xgUR}8 zV)ZNg^g+hPa(d#U%n5Oc8Upj>`MI>Oe)p;}pe|()tXzwIPbLjtu^w#mvb`Veb2$C> z4*%M$^_QX0Icc**#z*Eb`Ko6<8u0a3t*|OBdoHVxX-772JSLW=;q62E%cmv3?V_`O zLMexF|6()-n3Pg8Y!8mFj*A2paO;)mDb*?v+_V4G<>02+vxWawB7Ix9Hyfy1Tecn? z729X;_JD%>4OTV)UmuU`oeqa(%(C3@;Xf;>Y)RVSCFlKg06BS}z~n3Mlx>>PulL_} zDLS}Xx;u|0K_{!@Wv4jqY#De-2YzKOwPDY36RNt~x<7;pJnC9jg5C$3mJM{wv8TaN zS{#^iyV%ClCS`Fb!s$H$fZp9bDJHm{)Ne07l?w&BF>z{*A84S5{zcs@wsmeWt-P+8-t?j}_#Q^~*er46SAL2+YzHdlApUICM3EN^zoMh4#+J!NS^dVM=}}hhUE4 zNY;e)UZ`NDyi;PZo{OFdXz)-kVG1RIDLe}sd-r5qE)y8IZ*@MQfoAjeP#T(02AbfZ4Wl=QzYUX-lN)p?kAvk@fMov z_o_TN3*pby7Z$z}CUVi&{$&u=^Vincvt@&{a6{rnCcGks6-u(5tmc<9NoL!pgz%)? z6E$MwtX2XntB+9Qimxnlpjco3-l5&&Iiz zrIjKTdvQ-$cm&1A^@BX0Uz6yRDzkC*<)q-xLHTMn(}YQ>dY)=BnRn>tZvB+SHLH7* z(jcCgbsZc;hXfEQ zT|OK1mQcK$Mr65=m)FKLjEoB}V&D8y&MUPG}NJZYVJc6+8RPyeCtazqo7U>pIL zBG=L?eD4<}HBPvgfQrX2_9>VT9unnN1P%|L?}Ben0yqcZYsxuSAi-&W^RO<_kuA*+ z(yaJgg@tk~p5@VJNU$1M7>$v@ZjY)#&0iCivXipEXlv@$qcPa+3tU!WoE-w9F?9Z{ z*u)GvT6{oI?ZE{0s1WRN3r}^K0U1TY{ehL|RqTC~#Q52IV3ZcmfGL6@bm9t=yPEh-l zMtkjV_?(_XJfKG>KBsrP@hr~$igco{&INDaLU25-GCiw{&j}|OiStEZHoZciybm3B zoI0-5BGy+UDbZo;-*p`r`F^_j=az`-pcFoQ3EG$?#q1$Jd(CKg2%xrhW+tyPOcSky z+^+8_^saI8j(#otnaUn*&i3PJkv2HVOpxg(s+%{=?DIwI^ATl_ALN-*#&-8K#okgF z=r?-S?RPQj^l7eW6uiJL+RiINGgpF7$EaS=%dcF97%=lioQz`;&E$>NUn1+ApA-z9 zO`TFJ#@;~z6AK9GGMT^3faV6F&__uwJsY`e*z2+v3H&H1zG zYSyG^wX9e?q_@D`(*Hh26!^9n#XI`l6|d9Y)*8@?`0L|?%!ls_tY=2a)9seF@<_Ja zOlsp9E1!*IXQQ4qD3`f%NT~3&&0uvyzM2`6LmPb7>n%2x+49!;+3rR&Tn;Vea+Vhx z&vZH5YnSnIh#!9D%X`Y|a6Y3_E;CbJGp;rx){BC&nK6e^9(#s~vUw5jtv}BgA~ko43jZy~pN&vc z3;``zO#{knMHOpJE~kx9b!AD$Q54O5^I!k-x@R6bc;T=A>z5z<)wZ{z`Va1X=BwlH zZ~0XHA6|ay3p?)o_t%C;lJ@IEBgxU>k%?rvGE&j;%t$Maby`cO+74@bREN4;<@{ZG zB0lEitPUF0-QgZ>kLpnO4C`=Kfp4SQs??j!%1Gm!&wIL)p3FefJPUN0u72XMxWt6fl7*gk!c+Up!X@gs(HIu^Mm4Hb8jUT{5IwhFO%}Jb zE+3+6uLY%gGi)z}?Q21WE`;rCLn9MX9BJ8{w#jls{A$wd8`Vg9_6=cLQ8$vOh-geS zMNypWtJcFc5e97&jih~1*Xt9Y6eVNBx;!Sjno{P8=2&YuxiuUm?e8?zOEbAuyjl&t zt|F?{n!4VXw7**u`?UZ5+WrlsvRWCb>-H6pdeiLWO(9uF%e0BaWzb*Y?@FNQP06uN zQ?;2$&Z+SU4t6@H&9=^o7dk13$+P@PNdEeGZ zdb6Cg-%Z*-mh2}a)?^MP2%8f@HEp|-v!_wZIW{NcDr7gWOGDVnzJRLUu$oJ2uH$KN zycBqOsTxTME(Ha@RIPIybZ%CqF|bQLk4c-6mW{;(GXp-Hb1cFO1fzjXDRFfXFO;v|nvN>Z@C7!}4Ow zLb5)rch@Wgd=9HewG2JJsvaBTozIU%Lk-DfOKoU;A?X}eQ}EZF*h`}}(vacvqaEJk z+=zJdVxx%+_6bDNdAwOuzc1=QmtUmu#R3RrhKz z7zr-qP8iS;o^mdg(b}-!-lO|;^&Uo*v&mWc;^1Eo1-_jXrj-Un97;N8A>i!R+Hl~8 z6zWx7jfdFaapj_Ks%;ThCmMCQ8(5QcR^SdTS4L4GJkZ6D_X?M8oqi{i_V<7(PUsJ9+TY{e@y>;M9R22A1xb;>)SJ>pIrKovMM0^n zZ~4RF24Wo&s^c{^y`a-?N&`C=lg@Vlc##c9?F(CLRpGd%ECRMH`1b=o+7d_AxySY& zIH&}v@YAS7Y&IfGlNXo8u92FIjYdPEUkzCT`-Yg`l!Mphx*;z)o<_TOh9QH|YpAY4yDnw^Vbb~0 z)>>V{Zb;rk^~%VQ)GUnXK_vBUQ5BRywO)tp3Axzz#fgC2MXW<1hg6p9vL=Q))OJ>V z&ISet7t(pRDwi|S+*P}dAxJ-+w_)v%gRe+Bau~Lm#ry*B&fDJ0X*e-hz+ zNHEgLFyM-&3q%KE#0tH%zb6+AK5qrW-VfWKp-Y`#((f>tXoaak8*ZYE?oXajpV2%ntxCm z#a7#|)dgy$VczVPI7*gQmh`>CsgwG8t9_1nDURkUTMix9w@6>$!{_m5;%J|JfqAKQ z>{RR6nKQ>$S}#1;vX6?kPU-ukOD%oU(mrN7f4ueVN^AMV!ct40edHUVT1b+To5$E0 zM~$@}_4&u8g%?g0dpj0KPkC?4C+1sgw*_GHXVQMc$4gshm-InK^)}yHJoe=m&fL~o zTsXxpe3Dw&9&eqQ|MLaUmY5I zpoZw=`KoScCOf2uJ9LyhO+@ju)K`I&nzt{>jY$_Oa+zu?Hxcx`!68<8esXwZ{QS)I zty)u>ef}sg^bh9WY4qQ;fz>1=sPEe0m~Zur`W@ z-hT>FKMjZPRpUr*Ra^br7Du(m^krOqy$cYZ<%Q>3`(Ivod8)PlEN`5YJTKNd$jPAo5<{?wj5 zUtas%roIS!a^Z!gspIO0PS-xeDazw$EOdJ3%QM-^o9FB?hPaaDg7JAt#H`2`RB}i=|z3L_ssn#Paf6>b)&^4 zeTjB{e(JbBAjmrn;h8WK+1tQx=w%Gq?P*g97krE@*G{~x%!ZPkKPwY zKT$mF|9&!brw+fMN9CT;_Mj?bj_A!md}j6;?H^X|?~!opJH!9|zrY{WUrV+BXt9r? zzi5T9R7d#g3a2vv?$>cFsAQ!cSj%A@Qs#<$4FCQpw3^dZ7W;XdT3P@VZis#`d>a{` z@F1T*D$@ki$COX`yGXd6ul2~+&P)lP?gA~j_ehzL%bdAi_hcW>u)0cMe^&ja z?seIdYRrIWQ~{!s%ptKWOXiJ5T3c3SmO^tnxKkpcw{yqT6du9T8|UzCzj z*Ahjq3sx+SQny13xJqRVQnA$m6(e??;}OI!g@^F$I}3M(_d@Mg-&EkVRE@|YkH z9McoEgbtDV7t*d?OhN`!gPUMfa=4fVSur2F_7%0@UrWFD1$rM@gG$~M>|KZW3(Y9< zIHh|!$mmhEC#QH;)bL1>w<@+nHd}>T{{EtbQu*8=`P?m<&{lAzEwA{6VyfNjbH9VO z4?s3In;h8Wz$OPaIk3rrO%7~wV3Pxz9N6T*?>`5omAe%c|Fie^pOj4sHaW1#flUr< ha$u7Kn;h8Wz$OPaIk3rrO%7~wV3Pxz9O%t~{{s{B4zmCN literal 0 HcmV?d00001 diff --git a/Resources/.svn/text-base/desktop.ini b/Resources/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Resources/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Resources/.svn/text-base/zlib.dll.svn-base b/Resources/.svn/text-base/zlib.dll.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..2f5b3c7fc41b123458d186703e88fb7a43361874 GIT binary patch literal 65536 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 literal 0 HcmV?d00001 diff --git a/Resources/desktop.ini b/Resources/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/Resources/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/Resources/zlib.dll b/Resources/zlib.dll new file mode 100644 index 0000000000000000000000000000000000000000..2f5b3c7fc41b123458d186703e88fb7a43361874 GIT binary patch literal 65536 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 + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {CBD347BC-6BF8-417B-92B1-0BD44532A971} + Library + Properties + StayAlive + StayAlive + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/StayAlive/.svn/text-base/desktop.ini b/StayAlive/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/.svn/tmp/desktop.ini b/StayAlive/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/.svn/tmp/prop-base/desktop.ini b/StayAlive/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/.svn/tmp/props/desktop.ini b/StayAlive/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/.svn/tmp/text-base/desktop.ini b/StayAlive/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/Properties/.svn/all-wcprops b/StayAlive/Properties/.svn/all-wcprops new file mode 100644 index 0000000..45485b5 --- /dev/null +++ b/StayAlive/Properties/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/3/trunk/StayAlive/Properties +END +AssemblyInfo.cs +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/!svn/ver/3/trunk/StayAlive/Properties/AssemblyInfo.cs +END diff --git a/StayAlive/Properties/.svn/desktop.ini b/StayAlive/Properties/.svn/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/Properties/.svn/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/Properties/.svn/dir-prop-base b/StayAlive/Properties/.svn/dir-prop-base new file mode 100644 index 0000000..f1b96ea --- /dev/null +++ b/StayAlive/Properties/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 14 +bugtraq:number +V 4 +true +END diff --git a/StayAlive/Properties/.svn/entries b/StayAlive/Properties/.svn/entries new file mode 100644 index 0000000..55dfc4c --- /dev/null +++ b/StayAlive/Properties/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +58 +http://proxymud.googlecode.com/svn/trunk/StayAlive/Properties +http://proxymud.googlecode.com/svn + + + +2012-01-17T11:22:55.097115Z +3 +default8p@gmail.com +has-props + + + + + + + + + + + + + +8a6e9f07-fe1d-2472-fcf8-0b435762827a + +AssemblyInfo.cs +file + + + + +2016-03-25T22:18:42.960135Z +0b1389d5f8259145f5e25a0538b0dd64 +2012-01-17T11:22:55.097115Z +3 +default8p@gmail.com + + + + + + + + + + + + + + + + + + + + + +1430 + diff --git a/StayAlive/Properties/.svn/prop-base/desktop.ini b/StayAlive/Properties/.svn/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/Properties/.svn/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/Properties/.svn/props/desktop.ini b/StayAlive/Properties/.svn/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/Properties/.svn/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/Properties/.svn/text-base/AssemblyInfo.cs.svn-base b/StayAlive/Properties/.svn/text-base/AssemblyInfo.cs.svn-base new file mode 100644 index 0000000..799566b --- /dev/null +++ b/StayAlive/Properties/.svn/text-base/AssemblyInfo.cs.svn-base @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("StayAlive")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("StayAlive")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("d3850c5b-f9da-47e1-9451-9228e9f49f8f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/StayAlive/Properties/.svn/text-base/desktop.ini b/StayAlive/Properties/.svn/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/Properties/.svn/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/Properties/.svn/tmp/desktop.ini b/StayAlive/Properties/.svn/tmp/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/Properties/.svn/tmp/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/Properties/.svn/tmp/prop-base/desktop.ini b/StayAlive/Properties/.svn/tmp/prop-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/Properties/.svn/tmp/prop-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/Properties/.svn/tmp/props/desktop.ini b/StayAlive/Properties/.svn/tmp/props/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/Properties/.svn/tmp/props/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/Properties/.svn/tmp/text-base/desktop.ini b/StayAlive/Properties/.svn/tmp/text-base/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/Properties/.svn/tmp/text-base/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/Properties/AssemblyInfo.cs b/StayAlive/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..799566b --- /dev/null +++ b/StayAlive/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("StayAlive")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("StayAlive")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("d3850c5b-f9da-47e1-9451-9228e9f49f8f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/StayAlive/Properties/desktop.ini b/StayAlive/Properties/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/Properties/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/StayAlive/StayAlive.cs b/StayAlive/StayAlive.cs new file mode 100644 index 0000000..e60ed52 --- /dev/null +++ b/StayAlive/StayAlive.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ProxyCore; +using ProxyCore.Output; +using ProxyCore.Scripting; + +namespace StayAlive +{ + public class StayAlive : Plugin + { + public StayAlive() + : base("stayalive", "Stay logged in") + { + Author = "Duckbat"; + Version = 4; + Description = "Automatically stays logged in."; + UpdateUrl = "www.duckbat.com/plugins/update.stayalive.txt"; + Website = "code.google.com/p/proxymud/"; + + Config = new StayAliveConfig(); + + RegisterTrigger("autologin.name", "What be thy name, adventurer?", TriggerUsername); + RegisterTrigger("autologin.pass", "Existing profile loaded - please enter your password.", TriggerPassword); + } + + public override void OnConnect() + { + base.OnConnect(); + + CanEnterUsername = 1; + } + + private bool TriggerUsername(TriggerData t) + { + if(CanEnterUsername != 1) + return false; + + string n = Config.GetString("AutoLogin.User", "").Trim(); + if(string.IsNullOrEmpty(n)) + { + CanEnterUsername = 0; + return false; + } + + string p = Config.GetString("AutoLogin.Pass", "").Trim(); + if(string.IsNullOrEmpty(p)) + { + CanEnterUsername = 0; + return false; + } + + CanEnterUsername = 2; + World.Instance.Execute(n, false); + return false; + } + + private bool TriggerPassword(TriggerData t) + { + if(CanEnterUsername != 2) + return false; + + string p = Config.GetString("AutoLogin.Pass", "").Trim(); + if(string.IsNullOrEmpty(p)) + { + CanEnterUsername = 0; + return false; + } + + CanEnterUsername = 0; + World.Instance.Execute(p, false); + + World.Instance.Execute("", false); + return false; + } + + private int CanEnterUsername = 0; + + public override void OnLoadedConfig(bool Success) + { + base.OnLoadedConfig(Success); + + string n = Config.GetString("StayAlive.Line", "@wYour eyes glaze over."); + if(string.IsNullOrEmpty(n)) + return; + + RegisterTrigger("line", n, TriggerLine, TriggerFlags.NotRegex); + } + + private bool TriggerLine(TriggerData t) + { + World.Instance.Execute(Config.GetString("StayAlive.Command", ""), false); + return false; + } + } + + public class StayAliveConfig : ConfigFile + { + protected override void OnCreated() + { + base.OnCreated(); + + CreateSetting("StayAlive.Command", "", "What to send to MUD when we receive line about being idle. You can leave this blank to just send enter key."); + CreateSetting("StayAlive.Line", "@wYour eyes glaze over.", "On what line do we send command? Default: \"@wYour eyes glaze over.\""); + CreateSetting("AutoLogin.User", "", "User name to automatically log in with. Leave blank to disable this feature."); + CreateSetting("AutoLogin.Pass", "", "Password to automatically log in with. Leave blank to disable this feature."); + } + } +} diff --git a/StayAlive/StayAlive.csproj b/StayAlive/StayAlive.csproj new file mode 100644 index 0000000..5be8661 --- /dev/null +++ b/StayAlive/StayAlive.csproj @@ -0,0 +1,90 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {CBD347BC-6BF8-417B-92B1-0BD44532A971} + Library + Properties + StayAlive + StayAlive + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\Desktop\MushProxy-Build\Plugins\ + TRACE + prompt + 4 + + + + False + ..\ProxyCore\bin\ProxyCore.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/StayAlive/desktop.ini b/StayAlive/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/StayAlive/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/desktop.ini b/desktop.ini new file mode 100644 index 0000000..309dea2 --- /dev/null +++ b/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip=This folder is shared online. +IconFile=C:\Program Files (x86)\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/zlib.dll b/zlib.dll new file mode 100644 index 0000000000000000000000000000000000000000..2f5b3c7fc41b123458d186703e88fb7a43361874 GIT binary patch literal 65536 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