From 43941b4ee139a953ba74a17da11a8480b789ac27 Mon Sep 17 00:00:00 2001 From: chocochaos Date: Wed, 2 Jul 2014 11:06:24 +0200 Subject: [PATCH] Here's GM Genie! --- Chatreader.lua | 300 +++++++++++ Chronos/Chronos.lua | 860 ++++++++++++++++++++++++++++++ Chronos/Chronos.toc | 7 + Chronos/Chronos.xml | 24 + Chronos/localization.cn.lua | 10 + Chronos/localization.de.lua | 14 + Chronos/localization.en.lua | 13 + Chronos/localization.es.lua | 14 + Chronos/localization.fr.lua | 14 + Chronos/localization.kr.lua | 10 + Chronos/localization.ru.lua | 10 + Chronos/localization.tw.lua | 10 + GMGenie red.xml | 493 +++++++++++++++++ GMGenie.lua | 127 +++++ GMGenie.toc | 47 ++ GMGenie.xml | 440 +++++++++++++++ Hud.lua | 167 ++++++ Hud.xml | 131 +++++ Macros.Discipline.lua | 269 ++++++++++ Macros.Mail.lua | 73 +++ Macros.Tele.lua | 62 +++ Macros.Whispers.lua | 89 ++++ Macros.Whispers.xml | 53 ++ Macros.lua | 296 +++++++++++ Options/GMGenie.lua | 43 ++ Options/GMGenie.xml | 86 +++ Options/Macros.Discipline.lua | 297 +++++++++++ Options/Macros.Discipline.xml | 973 ++++++++++++++++++++++++++++++++++ Options/Macros.Mail.lua | 118 +++++ Options/Macros.Mail.xml | 237 +++++++++ Options/Macros.Tele.lua | 91 ++++ Options/Macros.Tele.xml | 215 ++++++++ Options/Macros.Whispers.lua | 112 ++++ Options/Macros.Whispers.xml | 190 +++++++ Options/Spawns.lua | 140 +++++ Options/Spawns.xml | 350 ++++++++++++ Options/Tickets.lua | 54 ++ Options/Tickets.xml | 55 ++ README.md | 201 ++++++- Reposts and modifications.txt | 12 + Savedvariables.lua | 40 ++ Spawns.lua | 621 ++++++++++++++++++++++ Spawns.xml | 632 ++++++++++++++++++++++ Spy.lua | 267 ++++++++++ Spy.xml | 573 ++++++++++++++++++++ Textures/Genie.tga | Bin 0 -> 945 bytes Tickets.lua | 496 +++++++++++++++++ Tickets.xml | 537 +++++++++++++++++++ 48 files changed, 9871 insertions(+), 2 deletions(-) create mode 100644 Chatreader.lua create mode 100644 Chronos/Chronos.lua create mode 100644 Chronos/Chronos.toc create mode 100644 Chronos/Chronos.xml create mode 100644 Chronos/localization.cn.lua create mode 100644 Chronos/localization.de.lua create mode 100644 Chronos/localization.en.lua create mode 100644 Chronos/localization.es.lua create mode 100644 Chronos/localization.fr.lua create mode 100644 Chronos/localization.kr.lua create mode 100644 Chronos/localization.ru.lua create mode 100644 Chronos/localization.tw.lua create mode 100644 GMGenie red.xml create mode 100644 GMGenie.lua create mode 100644 GMGenie.toc create mode 100644 GMGenie.xml create mode 100644 Hud.lua create mode 100644 Hud.xml create mode 100644 Macros.Discipline.lua create mode 100644 Macros.Mail.lua create mode 100644 Macros.Tele.lua create mode 100644 Macros.Whispers.lua create mode 100644 Macros.Whispers.xml create mode 100644 Macros.lua create mode 100644 Options/GMGenie.lua create mode 100644 Options/GMGenie.xml create mode 100644 Options/Macros.Discipline.lua create mode 100644 Options/Macros.Discipline.xml create mode 100644 Options/Macros.Mail.lua create mode 100644 Options/Macros.Mail.xml create mode 100644 Options/Macros.Tele.lua create mode 100644 Options/Macros.Tele.xml create mode 100644 Options/Macros.Whispers.lua create mode 100644 Options/Macros.Whispers.xml create mode 100644 Options/Spawns.lua create mode 100644 Options/Spawns.xml create mode 100644 Options/Tickets.lua create mode 100644 Options/Tickets.xml create mode 100644 Reposts and modifications.txt create mode 100644 Savedvariables.lua create mode 100644 Spawns.lua create mode 100644 Spawns.xml create mode 100644 Spy.lua create mode 100644 Spy.xml create mode 100644 Textures/Genie.tga create mode 100644 Tickets.lua create mode 100644 Tickets.xml diff --git a/Chatreader.lua b/Chatreader.lua new file mode 100644 index 0000000..3190c04 --- /dev/null +++ b/Chatreader.lua @@ -0,0 +1,300 @@ +--This file is part of Game Master Genie. +--Copyright 2011-2014 Chocochaos + +--Game Master Genie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +--Game Master Genie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +--You should have received a copy of the GNU General Public License along with Game Master Genie. If not, see . + +TicketTab = "General"; + +-- 1d2h3m4s to number in seconds +function GMGenie.timeStrToSeconds(timeStr) + local days = string.match(timeStr, "([0-9]*)d"); + if not days then + days = 0; + end + local hours = string.match(timeStr, "([0-9]*)h"); + if not hours then + hours = 0; + end + local minutes = string.match(timeStr, "([0-9]*)m"); + if not minutes then + minutes = 0; + end + local seconds = string.match(timeStr, "([0-9]*)s"); + if not seconds then + seconds = 0; + end + return (((((tonumber(days) * 24) + tonumber(hours)) * 60) + tonumber(minutes)) * 60) + tonumber(seconds); +end + +-- Read from chat +local ORIG_ChatFrame_MessageEventHandler = ChatFrame_MessageEventHandler; +function ChatFrame_MessageEventHandler(self, event, ...) + local arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15 = ...; + + local ActionTaken = false; + + -- development code to analize chat messages + --local excapedarg = string.gsub(arg1, "%|", "%%"); + --GMGenie.showGMMessage("1: " .. excapedarg); + + -- check for system messages of interest + if (event == "CHAT_MSG_SYSTEM") then + -- Showing list of open tickets whose creator is online. + if string.find(arg1, "Showing list of open tickets") then + Chronos.scheduleByName('ticketreupdate', 0.5, GMGenie.Tickets.update); + ActionTaken = true; + end + -- ticket list or reading ticket + local ticketId, name, createStr, lastModifiedStr, rest = string.match(arg1, "^%|cffaaffaaTicket%|r:%|cffaaccff%s([0-9]+).%|r%s%|cff00ff00Created%sby%|r:%|cff00ccff%s(.+)%|r%s%|cff00ff00Created%|r:%|cff00ccff%s([a-zA-Z0-9%s]+)%sago%|r%s%|cff00ff00Last%schange%|r:%|cff00ccff%s([a-zA-Z0-9%s]+)%sago%|r%s(.*)$"); + + if ticketId and name and createStr and lastModifiedStr then + ticketId = tonumber(ticketId); + local createStamp = GMGenie.timeStrToSeconds(createStr); + local lastModifiedStamp = GMGenie.timeStrToSeconds(lastModifiedStr); + if GMGenie.Tickets.tempList then + GMGenie.Tickets.listTicket(ticketId, name, createStr, createStamp, lastModifiedStr, lastModifiedStamp); + end + + local assignedTo = string.match(rest, "%|cff00ff00Assigned%sto%|r:%|cff00ccff%s([a-zA-Z]+)%|r%s"); + if assignedTo then + GMGenie.Tickets.setAssigned(ticketId, assignedTo); + end + local message = string.match(rest, "%|cff00ff00Ticket%sMessage%|r:%s%[(.-)%]%|r"); + local ticketCorrect = false; + if message then + ticketCorrect = GMGenie.Tickets.readTicket(ticketId, message); + else + local message = string.match(rest, "%|cff00ff00Ticket%sMessage%|r:%s%[(.*)"); + if message then + ticketCorrect = GMGenie.Tickets.readTicket(ticketId, message); + if ticketCorrect then + GMGenie.Tickets.messageOpen = true; + end + end + end + + local comment = string.match(rest, "%|cff00ff00GM%sComment%|r:%s%[(.*)%]%|r"); + if comment then + GMGenie.Tickets.comment(ticketId, comment); + end + + if ticketCorrect or GMGenie.Tickets.tempList then + ActionTaken = true; + end + elseif GMGenie.Tickets.messageOpen then + ActionTaken = true; + local message, rest = string.match(arg1, "(.-)%]%|r(.*)"); + if message then + GMGenie.Tickets.messageOpen = false; + GMGenie.Tickets.addLine(message); + else + if string.find(arg1, "%]%|r") then + rest = string.match(arg1, "%]%|r(.*)"); + GMGenie.Tickets.messageOpen = false; + else + GMGenie.Tickets.addLine(arg1); + end + end + + if rest then + local comment = string.match(rest, "%|cff00ff00GM%sComment%|r:%s%[(.*)%]%|r"); + if comment and GMGenie.Tickets.currentTicket['ticketId'] then + GMGenie.Tickets.comment(GMGenie.Tickets.currentTicket['ticketId'], comment); + end + end + else + -- Ticket edited + local name, ticketId = string.match(arg1, "^%|cff00ff00Character%|r%|cffff00ff%s([a-zA-Z]+)%s%|r%|cff00ff00edited%shis/her%sticket:%|r%|cffff00ff%s([0-9]+).%|r$"); + if name and ticketId then + if GMGenie.Tickets.isOpen() then + GMGenie.Tickets.refresh(); + end + ActionTaken = true; + end + -- Ticket abandoned + local name, ticketId = string.match(arg1, "^%|cff00ff00Character%|r%|cffff00ff%s([a-zA-Z]+)%s%|r%|cff00ff00abandoned%sticket%sentry:%|r%|cffff00ff%s([0-9]+).%|r$"); + if name and ticketId then + if GMGenie.Tickets.isOpen() then + GMGenie.Tickets.refresh(); + end + ActionTaken = true; + end + -- New Ticket + local name, ticketId = string.match(arg1, "^%|cff00ff00New%sticket%sfrom%|r%|cffff00ff%s([a-zA-Z]+).%|r%s%|cff00ff00Ticket%sentry:%|r%|cffff00ff%s([0-9]+).%|r$"); + if name and ticketId then + if GMGenie.Tickets.isOpen() then + GMGenie.Tickets.refresh(); + end + ActionTaken = true; + end + end + + -- read coords from chat + if GMGenie.Spawns.waitingForGps == 1 then + if string.find(arg1, "^You are outdoors") or string.find(arg1, "^no VMAP available for area info") then + ActionTaken = true; + end + local map = string.match(arg1, "^Map:%s([0-9]+)%s"); + if map then + GMGenie.Spawns.waitingForGps = 2; + GMGenie.Spawns.setMap(map); + ActionTaken = true; + end + end + if GMGenie.Spawns.waitingForGps == 2 then + local x, y, z, o = string.match(arg1, "^X:%s([0-9%.%-]+)%sY:%s([0-9%.%-]+)%sZ:%s([0-9%.%-]+)%sOrientation:%s([0-9%.%-]+)$"); + if x and y and z and o then + GMGenie.Spawns.waitingForGps = 3; + GMGenie.Spawns.move(x, y, z, o); + ActionTaken = true; + end + end + if GMGenie.Spawns.waitingForGps == 3 then + if string.find(arg1, "^grid") or string.find(arg1, "^ ZoneX") then + ActionTaken = true; + end + if string.find(arg1, "^GroundZ") then + GMGenie.Spawns.waitingForGps = 0; + ActionTaken = true; + end + end + + if GMGenie.Spy.waitingForPin or GMGenie.Macros.Discipline.IpBan.waitingForPin then + if string.find(arg1, "Player not found!") then + GMGenie.Spy.waitingForPin = false; + GMGenie.Macros.Discipline.IpBan.waitingForPin = false; + else + if GMGenie.Spy.waitingForPin then + local offline, name1, name2, guid = string.match(arg1, "Player ?(.*) %|cffffffff%|Hplayer:(.*)%|h%[(.*)%]%|h%|r %(guid: (.*)%)"); + local phase = string.match(arg1, "Phase: (.*)"); + local account, accountId, gmLevel = string.match(arg1, "Account: (.*) %(ID: (.*)%), GMLevel: (.*)"); + local login, failedLogins = string.match(arg1, "Last Login: (.*) %(Failed Logins: (.*)%)"); + local os, latency, email = string.match(arg1, "OS: (.*) %- Latency: (.*) ms %- Mail: (.*)"); + local ip, locked = string.match(arg1, "Last IP: (.*) %(Locked: (.*)%)"); + local level, xpCurrent, xpMax = string.match(arg1, "Level: (.*) %((.*)/(.*) XP"); + local race, class = string.match(arg1, "Race: (.*), (.*)"); + local alive = string.match(arg1, "Alive %?: (.*)"); + local money = string.match(arg1, "Money: (.*)"); + local map, area, zone = string.match(arg1, "Map: (.*), Area: (.*), Zone: (.*)"); + local guild, guildId = string.match(arg1, "Guild: (.*) %(ID: (.*)%)"); + local guildRank = string.match(arg1, "Rank: (.*)"); + local playedTime = string.match(arg1, "Played time: (.*)"); + + + if offline then + GMGenie.Spy.processPin01(offline, name1, guid, arg1); + ActionTaken = true; + end + if phase then + GMGenie.Spy.processPin02(phase, arg1); + ActionTaken = true; + end + if account then + GMGenie.Spy.processPin03(account, accountId, gmLevel, arg1); + ActionTaken = true; + end + if login then + GMGenie.Spy.processPin04(login, failedLogins, arg1); + ActionTaken = true; + end + if os then + GMGenie.Spy.processPin05(os, latency, email, arg1); + ActionTaken = true; + end + if ip then + GMGenie.Spy.processPin06(ip, locked, arg1); + ActionTaken = true; + end + if level then + GMGenie.Spy.processPin07(level, xpCurrent, xpMax, arg1); + ActionTaken = true; + end + if race then + GMGenie.Spy.processPin08(race, class, arg1); + ActionTaken = true; + end + if alive then + GMGenie.Spy.processPin09(alive, arg1); + ActionTaken = true; + end + if money then + GMGenie.Spy.processPin10(money, arg1); + ActionTaken = true; + end + if map then + GMGenie.Spy.processPin11(map, area, zone, arg1); + ActionTaken = true; + end + if guild then + GMGenie.Spy.processPin12(guild, guildId, arg1); + ActionTaken = true; + end + if guildRank then + GMGenie.Spy.processPin13(guildRank, arg1); + ActionTaken = true; + end + if playedTime then + GMGenie.Spy.processPin14(playedTime, arg1); + ActionTaken = true; + end + else + local ip, locked = string.match(arg1, "Last IP: (.*) %(Locked: (.*)%)") + + if ip then + GMGenie.Macros.Discipline.IpBan.processPin(ip); + ActionTaken = true; + end + end + end + end + + if GMGenie.Spawns.waitingForObject then + local name, guid, id = string.match(arg1, "%|cffffffff%|Hgameobject:.*%|h%[(.*)%]%|h%|r%sGUID:%s(.*)%sID:%s(.*)"); + if name and guid and id then + GMGenie.Spawns.deleteObject(name, guid, id); + ActionTaken = true; + elseif string.find(arg1, "X:%s.*%sY:%s.*%sZ:%s.*%sMapId:%s.*") or string.find(arg1, "Orientation:%s.*") or string.find(arg1, "Phasemask%s.*") then + ActionTaken = true; + elseif string.find(arg1, "SpawnTime:%sFull:.*%sRemain:.*") then + ActionTaken = true; + GMGenie.Spawns.waitingForObject = false; + elseif string.find(arg1, "Nothing found!") then + GMGenie.Spawns.waitingForObject = false; + end + end + + if GMGenie.Spawns.waitingForObjectDelete then + if string.find(arg1, "Game Object %(GUID: .*%) removed") then + ActionTaken = true; + GMGenie.Spawns.waitingForObjectDelete = false; + end + end + + local charName = UnitName("player"); + if string.match(arg1, "%|cffffffff%|Hplayer:" .. charName .. "%|h%[" .. charName .. "%]%|h%|r%'s Fly Mode on") then + GMGenie.Hud.flyStatus(true); + elseif string.match(arg1, "%|cffffffff%|Hplayer:" .. charName .. "%|h%[" .. charName .. "%]%|h%|r%'s Fly Mode off") then + GMGenie.Hud.flyStatus(false); + elseif arg1 == "Accepting Whisper: ON" or arg1 == "Accepting Whisper: on" then + GMGenie.Hud.whisperStatus(true); + elseif arg1 == "Accepting Whisper: OFF" or arg1 == "Accepting Whisper: off" then + GMGenie.Hud.whisperStatus(false); + elseif arg1 == "You are: visible" then + GMGenie.Hud.visibilityStatus(true); + elseif arg1 == "You are: invisible" then + GMGenie.Hud.visibilityStatus(false); + end + + local characterName = string.match(arg1, "%|cFFFFBF00%[AntiCheat%]%:%|cFFFFFFFF %[(.*)%] %|cFF00FFFFdetected as possible cheater%."); + if characterName then + arg1 = "|cFFFFBF00[AntiCheat]:|r |Hanticheat:" .. characterName .. "|h[" .. characterName .. "]|h detected as possible cheater."; + end + end + + -- if nothing was done, just display the message + if not ActionTaken then + ORIG_ChatFrame_MessageEventHandler(self, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15); + end +end \ No newline at end of file diff --git a/Chronos/Chronos.lua b/Chronos/Chronos.lua new file mode 100644 index 0000000..d36e99f --- /dev/null +++ b/Chronos/Chronos.lua @@ -0,0 +1,860 @@ +--[[ +-- +-- Chronos +-- Keeper of Time +-- +-- By AnduinLothar, Alexander Brazie, and Thott +-- +-- Chronos manages time. You can schedule a function to be called +-- in X seconds, with or without an id. You can request a timer, +-- which tracks the elapsed duration since the timer was started. +-- +-- To use as an embeddable addon: +-- - Put the Chronos folder inside your Interface/AddOns// folder. +-- - Add Chronos\Chronos.xml to your toc or load it in your xml before your localization files. +-- - Add Chronos to the OptionalDeps in your toc +-- +-- To use as an addon library: +-- - Put the Chronos folder inside your Interface/AddOns/ folder. +-- - Add Chronos to the Dependencies in your toc +-- +-- Please see below or see http://www.wowwiki.com/Chronos_(addon) for details. +-- +-- $LastChangedBy: karlkfi $ +-- $Date: 2006-12-21 06:19:14 -0600 (Thu, 21 Dec 2006) $ +-- $Rev: 4467 $ +-- +--]] + +local CHRONOS_REV = 2.12; + +local isBetterInstanceLoaded = (Chronos and Chronos.version and Chronos.version >= CHRONOS_REV); + +if (not isBetterInstanceLoaded) then + + if (not Chronos) then + Chronos = {}; + end + + Chronos.version = CHRONOS_REV; + + ------------------------------------------------------------------------------ + --[[ Variables ]] -- + ------------------------------------------------------------------------------ + + Chronos.online = true; + + CHRONOS_DEBUG = false; + CHRONOS_DEBUG_WARNINGS = false; + + -- Chronos Data + if (not ChronosData) then + ChronosData = {}; + end + + -- Chronos Recycled Tables Storage + if (not Chronos.tables) then + Chronos.tables = {}; + end + + -- Initialize the Timers + if (not ChronosData.timers) then + ChronosData.timers = {}; + end + + -- Initialize the perform-over-time task list + if (not ChronosData.tasks) then + ChronosData.tasks = {}; + end + + -- Maximum items per frame + Chronos.MAX_TASKS_PER_FRAME = 100; + + -- Maximum steps per task + Chronos.MAX_STEPS_PER_TASK = 300; + + -- Maximum time delay per frame + Chronos.MAX_TIME_PER_STEP = .3; + + Chronos.emptyTable = {}; + + ------------------------------------------------------------------------------ + --[[ User Functions ]] -- + ------------------------------------------------------------------------------ + + --[[ + -- debug(boolean) + -- + -- Toggles debug mode + ]] -- + function Chronos.debug(enable) + if (enable) then + ChronosFrame:SetScript("OnUpdate", Chronos.OnUpdate_Debug); + CHRONOS_DEBUG = true; + CHRONOS_DEBUG_WARNINGS = true; + else + ChronosFrame:SetScript("OnUpdate", Chronos.OnUpdate_Quick); + CHRONOS_DEBUG = false; + CHRONOS_DEBUG_WARNINGS = false; + end + end + + --[[ + -- Scheduling functions + -- Parts rewritten by AnduinLothar for efficiency + -- Parts rewritten by Thott for speed + -- Written by Alexander + -- Original by Thott + -- + -- Usage: Chronos.schedule(when,handler,arg1,arg2,etc) + -- + -- After seconds pass (values less than one and fractional values are + -- fine), handler is called with the specified arguments, i.e.: + -- handler(arg1,arg2,etc) + -- + -- If you'd like to have something done every X seconds, reschedule + -- it each time in the handler or preferably use scheduleRepeating. + -- + -- Also, please note that there is a limit to the number of + -- scheduled tasks that can be performed per xml object at the + -- same time. + --]] + function Chronos.schedule(when, handler, ...) + if (not Chronos.online) then + return; + end + if (not handler) then + Chronos.printError("ERROR: nil handler passed to Chronos.schedule()"); + return; + end + + --local memstart = collectgarbage("count"); + -- -- Assign an id + -- local id = ""; + -- if ( not this ) then + -- id = "Keybinding"; + -- else + -- id = self:GetName(); + -- end + -- if ( not id ) then + -- id = "_DEFAULT"; + -- end + -- if ( not when ) then + -- Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: ", id , " has sent no interval for this function. ", when ); + -- return; + -- end + + -- -- Ensure we're not looping ChronosFrame + -- if ( id == "ChronosFrame" and ChronosData.lastID ) then + -- id = ChronosData.lastID; + -- end + + -- use recycled tables to avoid excessive garbage collection -AnduinLothar + --tinsert(ChronosData.sched, Chronos.getTable()) + --local i = #ChronosData.sched + local recTable = Chronos.getTable() + -- ChronosData.sched[i].id = id; + recTable.time = when + GetTime(); + recTable.handler = handler; + recTable.args = Chronos.getArgTable(...); + + -- task list is a heap, add new + local i = #ChronosData.sched + 1 + while (i > 1) do + if (recTable.time < ChronosData.sched[i - 1].time) then + i = i - 1; + else + break + end + end + tinsert(ChronosData.sched, i, recTable) + + -- Debug print + --Chronos.printDebugError("CHRONOS_DEBUG", "Scheduled "..handler.." in "..when.." seconds from "..id ); + --Chronos.printError("Memory change in schedule: "..memstart.."->"..memend.." = "..memend-memstart); + end + + + --[[ + -- Chronos.scheduleByName(name, delay, function, arg1, ... ); + -- + -- Same as Chronos.schedule, except it takes a schedule name argument. + -- Only one event can be scheduled with a given name at any one time. + -- Thus if one exists, and another one is scheduled, the first one + -- is deleted, then the second one added. + -- + --]] + function Chronos.scheduleByName(name, when, handler, ...) + if (not name) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No name specified to Chronos.scheduleByName"); + return; + end + local namedSchedule = ChronosData.byName[name]; + if (namedSchedule and handler) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: scheduleByName is reasigning \"" .. name .. "\"."); + Chronos.releaseTable(ChronosData.byName[name]); + else + if (not handler) then + if (not namedSchedule) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No handler specified to Chronos.scheduleByName, no previous entry found for scheduled entry \"" .. name .. "\"."); + return; + end + if (not namedSchedule.handler) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No handler specified to Chronos.scheduleByName, no handler could be found in previous entry of \"" .. name .. "\" either."); + return; + end + handler = namedSchedule.handler; + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: scheduleByName is updating \"" .. name .. "\" to time: " .. when); + else + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: scheduleByName is asigning \"" .. name .. "\"."); + end + end + ChronosData.byName[name] = Chronos.getTable(); + namedSchedule = ChronosData.byName[name]; + namedSchedule.time = when + GetTime() + namedSchedule.handler = handler; + namedSchedule.args = Chronos.getArgTable(...); + end + + --[[ + -- unscheduleByName(name); + -- + -- Removes an entry that was created with scheduleByName() + -- + -- Args: + -- name - the name used + -- + --]] + function Chronos.unscheduleByName(name) + if (not Chronos.online) then + return; + end + if (not name) then + Chronos.printError("No name specified to Chronos.unscheduleByName"); + return; + end + if (ChronosData.byName[name]) then + Chronos.releaseTable(ChronosData.byName[name]); + ChronosData.byName[name] = nil; + end + + -- Debug print + --Chronos.printDebugError("CHRONOS_DEBUG", "Cancelled scheduled timer of name ",name); + end + + --[[ + -- unscheduleRepeating(name); + -- Mirrors unscheduleByName for backwards compatibility + --]] + Chronos.unscheduleRepeating = Chronos.unscheduleByName; + + --[[ + -- isScheduledByName(name) + -- Returns the amount of time left if it is indeed scheduled by name! + -- + -- returns: + -- number - time remaining + -- nil - not scheduled + -- + --]] + function Chronos.isScheduledByName(name) + if (not Chronos.online) then + return; + end + if (not name) then + Chronos.printError("No name specified to Chronos.isScheduledByName " .. (self:GetName() or "unknown")); + return; + end + local namedSchedule = ChronosData.byName[name]; + if (namedSchedule) then + return namedSchedule.time - GetTime(); + end + + -- Debug print + --Chronos.printDebugError("CHRONOS_DEBUG", "Did not find timer of name ",name); + return nil; + end + + --[[ + -- isScheduledRepeating(name) + -- Mirrors isScheduledByName for backwards compatibility + --]] + Chronos.isScheduledRepeating = Chronos.isScheduledByName; + + --[[ + -- Chronos.scheduleRepeating(name, delay, function); + -- + -- Same as Chronos.scheduleByName, except it repeats without recalling and takes no arguments. + -- + --]] + function Chronos.scheduleRepeating(name, when, handler) + if (not name) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No name specified to Chronos.scheduleRepeating"); + return; + end + local namedSchedule = ChronosData.byName[name]; + if (namedSchedule and handler) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: scheduleRepeating is reasigning " .. name); + Chronos.releaseTable(ChronosData.byName[name]); + else + if (not handler) then + if (not namedSchedule) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No handler specified to Chronos.scheduleRepeating, no previous entry found for scheduled entry '" .. name .. "'."); + return; + end + if (not namedSchedule.handler) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No handler specified to Chronos.scheduleRepeating, no handler could be found in previous entry '" .. name .. "' either."); + return; + end + handler = namedSchedule.handler; + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: scheduleRepeating is updating '" .. name .. "' to time: " .. when); + else + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: scheduleRepeating is asigning '" .. name .. "'."); + end + end + ChronosData.byName[name] = Chronos.getTable(); + namedSchedule = ChronosData.byName[name]; + namedSchedule.time = when + GetTime(); + namedSchedule.period = when; + namedSchedule.handler = handler; + namedSchedule.repeating = true; + end + + --[[ + -- Chronos.flushByName(name, when); + -- + -- Updates the ByName or Repeating event to flush at the time specified. If no time is specified flush will be immediate. If it is a Repeating event the timer will be reset. + -- + --]] + function Chronos.flushByName(name, when) + if (not name) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No name specified to Chronos.flushByName"); + return; + elseif (not ChronosData.byName[name]) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: no previous entry found for Chronos.flushByName entry '" .. name .. "'."); + return; + end + if (not when) then + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: flushing '" .. name .. "'."); + when = GetTime(); + else + Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: flushing '" .. name .. "' in " .. when .. " seconds."); + when = when + GetTime(); + end + ChronosData.byName[name].time = when; + end + + --[[ + -- Chronos.startTimer([ID]); + -- Starts a timer on a particular + -- + -- Args + -- ID - optional parameter to identify who is asking for a timer. + -- + -- If ID does not exist, self:GetName() is used. + -- + -- When you want to get the amount of time passed since startTimer(ID) is called, + -- call getTimer(ID) and it will return the number in seconds. + -- + --]] + function Chronos.startTimer(id) + if (not Chronos.online) then + return; + end + + if (not id) then + id = self:GetName(); + end + + -- Create a table for this id's timers + if (not ChronosData.timers[id]) then + ChronosData.timers[id] = Chronos.getTable(); + end + + -- Clear out an entry if the table is too big. + if (#ChronosData.timers[id] > Chronos.MAX_TASKS_PER_FRAME) then + Chronos.printError("Too many Chronos timers created for id " .. tostring(id)); + return; + end + + -- Add a new timer entry + table.insert(ChronosData.timers[id], GetTime()); + end + + + --[[ + -- endTimer([id]); + -- + -- Ends the timer and returns the amount of time passed. + -- + -- args: + -- id - ID for the timer. If not specified, then ID will + -- be self:GetName() + -- + -- returns: + -- (Number delta, Number start, Number end) + -- + -- delta - the amount of time passed in seconds. + -- start - the starting time + -- now - the time the endTimer was called. + --]] + + function Chronos.endTimer(id) + if (not Chronos.online) then + return; + end + + if (not id) then + id = self:GetName(); + end + + if (not ChronosData.timers[id] or #ChronosData.timers[id] == 0) then + return nil; + end + + local now = GetTime(); + + -- Grab the last timer called + local startTime = tremove(ChronosData.timers[id]); + + return (now - startTime), startTime, now; + end + + + --[[ + -- getTimer([id]); + -- + -- Gets the timer and returns the amount of time passed. + -- Does not terminate the timer. + -- + -- args: + -- id - ID for the timer. If not specified, then ID will + -- be self:GetName() + -- + -- returns: + -- (Number delta, Number start, Number end) + -- + -- delta - the amount of time passed in seconds. + -- start - the starting time + -- now - the time the endTimer was called. + --]] + + function Chronos.getTimer(id) + if (not Chronos.online) then + return; + end + + if (not id) then + id = self:GetName(); + end + + local now = GetTime(); + if (not ChronosData.timers[id] or #ChronosData.timers[id] == 0) then + return 0, 0, now; + end + + -- Grab the last timer called + local startTime = ChronosData.timers[id][#ChronosData.timers[id]]; + + return (now - startTime), startTime, now; + end + + --[[ + -- isTimerActive([id]) + -- returns true if the timer exists. + -- + -- args: + -- id - ID for the timer. If not specified, then ID will + -- be self:GetName() + -- + -- returns: + -- true - exists + -- false - does not + --]] + function Chronos.isTimerActive(id) + if (not Chronos.online) then + return; + end + + if (not id) then + id = self:GetName(); + end + + -- Create a table for this id's timers + if (not ChronosData.timers[id]) then + return false; + end + + return true; + end + + --[[ + -- getTime() + -- + -- returns the Chronos internal elapsed time. + -- + -- returns: + -- (elapsedTime) + -- + -- elapsedTime - time in seconds since Chronos initialized + --]] + function Chronos.getTime() + return ChronosData.elapsedTime; + end + + --[[ + -- Chronos.afterInit(func, ...) + -- Performs func after the game has truely started. + -- By Thott + --]] + function Chronos.afterInit(func, ...) + local id; + if (this) then + id = self:GetName(); + else + id = "unknown"; + end + --if(id == "SkyFrame") then + -- Chronos.printError("Ignoring Sky init"); + -- return; + --end + if (ChronosData.initialized) then + func(...); + else + if (not ChronosData.afterInit) then + ChronosData.afterInit = Chronos.getTable(); + Chronos.schedule(0.2, Chronos.initCheck); + end + local recTable = Chronos.getTable(); + recTable.func = func; + recTable.args = Chronos.getArgTable(...); + recTable.id = id; + tinsert(ChronosData.afterInit, recTable); + end + end + + + ------------------------------------------------------------------------------ + --[[ Table Recycling ]] -- + ------------------------------------------------------------------------------ + function Chronos.getTable(...) + local stack = Chronos.tables; + if (not stack) then + Chronos.tables = {}; + stack = Chronos.tables; + return {}; + end + local recTable; + if (#stack >= 1) then + recTable = tremove(stack) + else + recTable = {}; + end + for i = 1, select("#", ...) do + recTable[i] = select(i, ...); + end + return recTable; + end + + -- Release a table to be nilled and used again. + -- Optionally pass in an unpack(...) as the 2nd arg so that you can return the args: + -- return Chronos.releaseTable(t1, unpack(t1)) + function Chronos.releaseTable(t1, ...) + if (type(t1) ~= "table") then + return; + end + + local stack = Chronos.tables; + if (not stack) then + Chronos.tables = {}; + stack = Chronos.tables; + end + + for k, v in pairs(t1) do + t1[k] = nil; + end + + tinsert(stack, t1); + return ...; + end + + + ------------------------------------------------------------------------------ + --[[ Helpers Functions ]] -- + ------------------------------------------------------------------------------ + function Chronos.getArgTable(...) + if (select('#', ...) == 0) then + return Chronos.emptyTable; + else + return Chronos.getTable(...); + end + end + + function Chronos.run(func, args) + if (func) then + if (args) then + return func(unpack(args)); + else + return func(); + end + end + end + + function Chronos.printError(text) + ChatFrame1:AddMessage(text, RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b, 1.0, UIERRORS_HOLD_TIME); + end + + function Chronos.printDebugError(var, text) + if (var) and (getglobal(var)) then + Chronos.printError(text); + end + end + + ------------------------------------------------------------------------------ + --[[ Frame Script Helpers ]] -- + ------------------------------------------------------------------------------ + function Chronos.chatColorsInit() + ChronosData.chatColorsInitialized = true; + ChronosFrame:UnregisterEvent("UPDATE_CHAT_COLOR"); + end + + function Chronos.initCheck() + if (not ChronosData.initialized) then + if (UnitName("player") and UnitName("player") ~= UKNOWNBEING and UnitName("player") ~= UNKNOWNBEING and UnitName("player") ~= UNKNOWNOBJECT and ChronosData.variablesLoaded and ChronosData.enteredWorld and ChronosData.chatColorsInitialized) then + ChronosData.initialized = true; + Chronos.schedule(1, Chronos.initCheck); + return; + else + Chronos.schedule(0.2, Chronos.initCheck); + return; + end + end + if (ChronosData.afterInit) then + local i = ChronosData.afterInit_i; + if (not i) then + i = 1; + end + ChronosData.afterInit_i = i + 1; + --Chronos.printError("afterInit: processing ",i," of ",ChronosData.afterInit.n," initialization functions, id: ",ChronosData.afterInit[i].id); + Chronos.run(ChronosData.afterInit[i].func, ChronosData.afterInit[i].args); + if (i == #ChronosData.afterInit) then + for i, v in ipairs(ChronosData.afterInit) do + Chronos.releaseTable(v); + end + Chronos.releaseTable(ChronosData.afterInit); + ChronosData.afterInit = nil; + ChronosData.afterInit_i = nil; + else + Chronos.schedule(0.1, Chronos.initCheck); + return; + end + end + end + + --[[ + -- Sends a chat command through the standard editbox + --]] + function Chronos.SendChatCommand(command) + local text = ChatFrameEditBox:GetText(); + ChatFrameEditBox:SetText(command); + ChatEdit_SendText(ChatFrameEditBox); + ChatFrameEditBox:SetText(text); + end + + function Chronos.RegisterSlashCommands() + --Needs to be able Variables load if you want to use Sky + local chronosFunc = function(msg) + local _, _, seconds, command = string.find(msg, "([%d\.]+)%s+(.*)"); + if (seconds and command) then + Chronos.schedule(seconds, Chronos.SendChatCommand, command); + else + Chronos.printError(SCHEDULE_USAGE1); + Chronos.printError(SCHEDULE_USAGE2); + end + end + if (Satellite) then + Satellite.registerSlashCommand({ + id = "Schedule"; + commands = SCHEDULE_COMM; + onExecute = chronosFunc; + helpText = SCHEDULE_DESC; + replace = true; + }); + else + SlashCmdList["CHRONOS_SCHEDULE"] = chronosFunc; + for i = 1, #SCHEDULE_COMM do setglobal("SLASH_CHRONOS_SCHEDULE" .. i, SCHEDULE_COMM[i]); end + end + end + + ------------------------------------------------------------------------------ + --[[ Frame Scripts ]] -- + ------------------------------------------------------------------------------ + function Chronos.OnLoad() + Chronos.framecount = 0; + + if (not ChronosData.byName) then + ChronosData.byName = {}; + end + if (not ChronosData.repeating) then + ChronosData.repeating = {}; + end + if (not ChronosData.sched) then + ChronosData.sched = {}; + end + ChronosData.elapsedTime = 0; + + Chronos.afterInit(Chronos.RegisterSlashCommands); + end + + function Chronos.OnEvent(self, event, ...) + if (event == "ADDON_LOADED") then + ChronosData.variablesLoaded = true; + ChronosFrame:Show(); + elseif (event == "PLAYER_ENTERING_WORLD") then + ChronosData.enteredWorld = true; + Chronos.online = true; + elseif (event == "PLAYER_LEAVING_WORLD") then + Chronos.online = false; + elseif (event == "UPDATE_CHAT_COLOR") then + Chronos.scheduleByName("ChronosAfterChatColorInit", 1, Chronos.chatColorsInit); + end + end + + function Chronos.OnUpdate_Quick(self, arg1) + if (not Chronos.online) then + return; + end + if (not ChronosData.variablesLoaded) then + return; + end + + if (ChronosData.elapsedTime) then + ChronosData.elapsedTime = ChronosData.elapsedTime + arg1; + else + ChronosData.elapsedTime = arg1; + end + + -- Execute scheduled tasks that are ready, pulling them off the front of the list queue. + local now = GetTime(); + local i; + local task; + while (#ChronosData.sched > 0) do + if (not ChronosData.sched[1].time) then + --Sea.io.printTable(ChronosData.sched[1]); + tremove(ChronosData.sched, 1); + elseif (ChronosData.sched[1].time <= now) then + task = tremove(ChronosData.sched, 1); + Chronos.run(task.handler, task.args); + Chronos.releaseTable(task); + else + break; + end + end + + -- Execute named scheduled tasks that are ready. + local k, v = next(ChronosData.byName); + local newK, newV; + while (k ~= nil) do + newK, newV = next(ChronosData.byName, k); + if (not v.time) then + --Sea.io.printTable(v); + ChronosData.byName[k] = nil; + elseif (v.time <= now) then + if (v.repeating) then + ChronosData.byName[k].time = now + v.period; + v.handler(); + else + Chronos.run(v.handler, v.args); + Chronos.releaseTable(ChronosData.byName[k]); + ChronosData.byName[k] = nil; + end + end + k, v = newK, newV; + end + end + + function Chronos.OnUpdate_Debug(self, arg1) + if (not Chronos.online) then + return; + end + if (not ChronosData.variablesLoaded) then + return; + end + local memstart = collectgarbage("count"); + + if (ChronosData.elapsedTime) then + ChronosData.elapsedTime = ChronosData.elapsedTime + arg1; + else + ChronosData.elapsedTime = arg1; + end + + local now = GetTime(); + local i; + local task; + -- Execute scheduled tasks that are ready, popping them off the heap. + while (#ChronosData.sched > 0) do + if (ChronosData.sched[1].time <= now) then + task = tremove(ChronosData.sched, 1); + Chronos.run(task.handler, task.args); + Chronos.releaseTable(task); + else + break; + end + end + + local memend = collectgarbage("count"); + if (memend - memstart > 0) then + Chronos.printError("gcmemleak from ChronosData.sched in OnUpdate: " .. (memend - memstart)); + end + + -- Execute named scheduled tasks that are ready. + memstart = memend; + local k, v = next(ChronosData.byName); + local newK, newV; + while (k ~= nil) do + newK, newV = next(ChronosData.byName, k); + if (v.time <= now) then + local m = collectgarbage("count"); + if (v.repeating) then + ChronosData.byName[k].time = now + v.period; + v.handler(); + else + Chronos.run(v.handler, v.args); + Chronos.releaseTable(ChronosData.byName[k]); + ChronosData.byName[k] = nil; + end + local mm = collectgarbage("count"); + memstart = memstart + mm - m; + end + k, v = newK, newV; + end + + memend = collectgarbage("count"); + if (memend - memstart > 0) then + Chronos.printError("gcmemleak from ChronosData.byName in OnUpdate: " .. (memend - memstart)); + end + end + + ------------------------------------------------------------------------------ + --[[ Frame Script Assignment ]] -- + ------------------------------------------------------------------------------ + + Chronos.OnUpdate_Quick(); + + --Event Driver + if (not ChronosFrame) then + CreateFrame("Frame", "ChronosFrame"); + end + ChronosFrame:Hide(); + --Event Registration + ChronosFrame:RegisterEvent("ADDON_LOADED"); + ChronosFrame:RegisterEvent("PLAYER_ENTERING_WORLD"); + ChronosFrame:RegisterEvent("PLAYER_LEAVING_WORLD"); + ChronosFrame:RegisterEvent("UPDATE_CHAT_COLOR"); + --Frame Scripts + ChronosFrame:SetScript("OnEvent", Chronos.OnEvent); + ChronosFrame:SetScript("OnUpdate", Chronos.OnUpdate_Quick); + --OnLoad Call + Chronos.OnLoad(); +end + diff --git a/Chronos/Chronos.toc b/Chronos/Chronos.toc new file mode 100644 index 0000000..52d3cb2 --- /dev/null +++ b/Chronos/Chronos.toc @@ -0,0 +1,7 @@ +## Interface: 20400 +## Title: Chronos +## Notes: Embeddable Time Keeping and Scheduling System +## Notes-deDE: Sammlung nützlicher Funktionen zum Zeitmanagement, welche von einigen anderen AddOns verwendet werden. +## Author: Alexander Brazie, Thott, AnduinLothar +## X-Website: http://www.wowinterface.com/downloads/info4328-Chronos.html +Chronos.xml \ No newline at end of file diff --git a/Chronos/Chronos.xml b/Chronos/Chronos.xml new file mode 100644 index 0000000..f727726 --- /dev/null +++ b/Chronos/Chronos.xml @@ -0,0 +1,24 @@ + + + + + + + + +