GUILDADSCOMM_DEBUG = false; -- Is debug printf enabled? GUILDADS_MSG_TYPE_ANNONCE = 0; GUILDADS_MSG_TYPE_REQUEST = 1; GUILDADS_MSG_TYPE_AVAILABLE = 2; GUILDADS_MSG_TYPE_SKILL = 3; GUILDADS_MSG_TYPE_EVENT = 4; GUILDADS_MSG_TYPE_INVENTORY = 5; GUILDADS_MSG_TYPE_NOTE = 6; GUILDADS_MSG_TYPE_EVENT_SUBSCRIPTION = 7; GUILDADS_MSG_TYPE_IGNORE = "i"; GUILDADS_VERSION_PROTOCOL = "26"; GUILDADS_MSG_PREFIX = ""; GUILDADS_MSG_PREFIX_NOVERSION = "'; GUILDADS_MSG_ADD = "a"; GUILDADS_MSG_REMOVE = "r"; GUILDADS_MSG_REMOVE_ALL = "R"; GUILDADS_MSG_REQUEST_ADS = "?"; GUILDADS_MSG_REQUEST_INSPECT = "?i"; GUILDADS_MSG_REQUEST_OFFLINES = "?o"; GUILDADS_MSG_SENDING_UPDATE = "U"; GUILDADS_MSG_SENDING_ALL = "S"; GUILDADS_MSG_SENDING_ALL_END = "E"; GUILDADS_MSG_LASTSEEN = "l"; GUILDADS_MSG_LASTSEEN_END = "le"; GUILDADS_MSG_META = "m"; GUILDADS_MSG_CHATFLAG = "chatFlag"; GUILDADS_STATE_UNKNOW = "unknow"; GUILDADS_STATE_SYNC_ONLINE = "s_online"; GUILDADS_STATE_SYNC_OFFLINE = "s_offline"; GUILDADS_STATE_OK = "ok"; local playerName = ""; local MonitorAds = {}; -- record updated ads (GUILDADS_MSG_SENDING_ALL) local MetaPlayers = {}; -- state, onlineSince, version local MyState = GUILDADS_STATE_UNKNOW; local LastSeens = {}; -- Players we are listen to give theirs offline players. local WaitingOfflinesAds = { }; -- Players are we waiting for offlines ads to be GUILDADS_STATE_OK local StartTime; local OnMessageCommand = { }; local OnMessageAd = {}; local WatingForUpdate = { }; local function DEBUG_MSG(msg) if (GUILDADSCOMM_DEBUG) then ChatFrame1:AddMessage("GAC: "..msg, 1.0, 1.0, 0.5); end end local function GAC_GetGuildChatFrame() for i=1,NUM_CHAT_WINDOWS,1 do local DefaultMessages = { GetChatWindowMessages(i) }; for k, channel in DefaultMessages do if channel == "GUILD" then return getglobal("ChatFrame"..i); end end end return DEFAULT_CHAT_FRAME; end function GAC_GetMeta(player) return MetaPlayers[player]; end function GAC_GetMetas() return MetaPlayers; end GAC_GetFlag = SimpleComm_GetFlag; function GAC_AddChatMessage(msg) local info = ChatTypeInfo["CHANNEL"..GetChannelName( SimpleComm_Channel )]; SimpleComm_ChatFrame:AddMessage(msg, info.r, info.g, info.b); end function GAC_InitVariablesLoaded() SimpleComm_PreInit(GuildAds_FilterText); end function GAC_Init(playername, channel, password) DEBUG_MSG("GAC_Init("..playername..","..channel..")"); playerName = playername; if (not StartTime) then StartTime = GAS_currentTime(); end SimpleComm_Init( channel, password, GAC_GetGuildChatFrame(), GAC_Synchronize, GAC_OnChannelLeave, GAC_OnMessage, GuildAds_Serialize, GuildAds_Unserialize, GuildAds_FilterText ); local command, alias = GuildAdsConfig_GetChannelAlias() SimpleComm_InitAlias(command, alias); SimpleComm_SetFlagListener(GAC_OnChatFlagChange); -- Init after the channel is joined (GAC_Synchronize called by SimpleComm) end function GAC_Reinit(channel, password) -- Reset internal variables MonitorAds = {}; LastSeens = {}; WaitingOfflinesAds = { }; WatingForUpdate = { }; MyState = GUILDADS_STATE_UNKNOW; GuildAdsSystem.SynchronizeOfflinesTimer = nil; GuildAdsSystem.SynchronizeOfflinesTimerEnd = nil; -- Reinit GuildAdsComm SimpleComm_SetChannel(channel, password); -- Init after the channel is joined (GAC_Synchronize called by SimpleComm) end function GAC_OnChannelLeave() GuildAdsPlugin_OnChannelLeave(); end function GAC_Synchronize() DEBUG_MSG("GAC_Synchronize"); -- call plugin init GuildAdsPlugin_OnChannelJoin(); -- reset local variables MonitorAds = {}; MetaPlayers = {}; LastSeens = {}; WaitingOfflinesAds = {}; WatingForUpdate = { }; -- Send status MyState = GUILDADS_STATE_SYNC_ONLINE; GAC_SendMeta(nil); -- Send chat status : detected by SimpleComm GAC_SendChatFlag(playerName); -- Now, send to all my ads GAC_SendAllAdsType(nil, nil, nil); -- Ask everyone to send me their ads GAC_SendRequestAds(nil); -- Wait 30 seconds and synchronize offlines GuildAdsSystem.SynchronizeOfflinesCount = 0; GuildAdsSystem.SynchronizeOfflinesTimer = 30; GuildAdsSystem.SynchronizeOfflinesTimerEnd = false; end function GAC_SynchronizeOfflines(numberOfTries) DEBUG_MSG("GAC_SynchronizeOfflines("..numberOfTries..")"); --[[ moi à player> envois de GUILDADS_MSG_REQUEST_OFFLINES player à moi> envois de GUILDADS_MSG_LASTSEEN pour chacun de ses offlines moi> pour chaque GUILDADS_MSG_LASTSEEN reçu : si plus ancien -> envois de mes annonces si plus récent -> demande les annonces à player player à moi> envois de GUILDADS_MSG_LASTSEEN_END envois de ses offlines non mis à jour ]] -- Ask offlines to someone who is online local oneOnlinePlayer, canTryLater = GAC_GetRandomOnline(); if (oneOnlinePlayer) then -- Il y au moins un autre joueur if (canTryLater) and (numberOfTries<=20) then --[[ aucun joueur connecté n'est synchronisé attends à nouveau 30 secondes sauf s'il y a plus de 20 essais (donc 10 minutes) nombre d'essais limité pour éviter le deadlock si deux joueurs se connectent au même moment onelinePlayer = true ]] GuildAdsSystem.SynchronizeOfflinesTimerEnd = false; GuildAdsSystem.SynchronizeOfflinesCount = numberOfTries+1; GuildAdsSystem.SynchronizeOfflinesTimer = 30; else -- Changement d'état MyState = GUILDADS_STATE_SYNC_OFFLINE; GAC_SendMeta(nil); -- Il y a au moins un autre joueur connecté GAC_SendRequestOfflines(oneOnlinePlayer); -- 10 minutes avant d'être déclaré synchronisé GuildAdsSystem.SynchronizeOfflinesTimer = 60*10; GuildAdsSystem.SynchronizeOfflinesCount = 0; GuildAdsSystem.SynchronizeOfflinesTimerEnd = true; end else -- Aucun online -- Synchronisation terminée MyState = GUILDADS_STATE_OK; GAC_SendMeta(nil); end end function GAC_SynchronizeOfflinesEnd() MyState = GUILDADS_STATE_OK; GAC_SendMeta(nil); end function GAC_OnChatFlagChange(flag, message) SimpleComm_SendMessage( nil, { command = GUILDADS_MSG_CHATFLAG; flag = flag; text = message; } ); end --[[ Retourne le nom d'un joueur connecté et synchronisé. Si aucun joueur connecté n'est synchronisé, retourne true Si aucun joueur n'est connecté, retourne nil ]] function GAC_GetRandomOnline() local canTryLater = false; local ready = {}; for name, metainfo in MetaPlayers do if (name ~= playerName) then if (metainfo.state == GUILDADS_STATE_OK) then tinsert(ready, name); elseif (metainfo.state == GUILDADS_STATE_SYNC_OFFLINE) or (metainfo.state == GUILDADS_STATE_SYNC_ONLINE) then canTryLater = true; end end end local s = table.getn(ready); if (s > 0) then return ready[math.random(s)], false; else if (canTryLater) then return nil, true; else return nil, false; end end end function GAC_SendAd(who, ad_type ,ad, delay) if (ad.m_Enabled == false) then return; end SimpleComm_SendMessage( who, { command = GUILDADS_MSG_ADD; adtype = ad_type; id = ad.id; text = ad.text; texture = ad.texture; count = ad.count; itemRef = ad.itemRef; itemName = ad.itemName; itemColor = ad.itemColor; skillRank = ad.skillRank; skillMaxRank = ad.skillMaxRank; creationtime = ad.creationtime; owner = ad.owner }, delay ); end function GAC_SendSkill(who, id, skillRank, skillMaxRank, delay) SimpleComm_SendMessage( who, { command = GUILDADS_MSG_ADD; adtype = GUILDADS_MSG_TYPE_SKILL; id = id; skillRank = skillRank; skillMaxRank = skillMaxRank; creationtime = GAS_currentTime() }, delay ); end function GAC_SendSkills(who, delay) local grp = ""; for i = 1, GetNumSkillLines(), 1 do local skillName, header, isExpanded, skillRank, numTempPoints, skillModifier, skillMaxRank, isAbandonable, stepCost, rankCost, minLevel, skillCostType = GetSkillLineInfo(i); if (header == 1) then grp = skillName; else local id = GAS_GetSkillId(skillName); if (id > 0) then GAC_SendSkill(who, id, skillRank, skillMaxRank, delay) end end end end function GAC_SendInventory(who, slot, texture, color, ref, name, count) SimpleComm_SendMessage( who, { command = GUILDADS_MSG_ADD; adtype = GUILDADS_MSG_TYPE_INVENTORY; id = slot; itemRef = ref; itemName = name; itemColor = color; count = count; texture = texture; creationtime = GAS_currentTime() } ); end function GAC_SendInspect(who) local buffer = {}; for slot=1, 19, 1 do local link = GetInventoryItemLink("player", slot); if (link) then -- local title = TEXT(getglobal(strupper(SlotIdText[slot]))); local texture = GetInventoryItemTexture("player", slot); local count = GetInventoryItemCount("player", slot); local color, ref, name = GAS_UnpackLink(link); tinsert(buffer, { slot = slot, texture = texture, color = color, ref = ref, name = name, count = count } ); end end GAC_SendingUpdate(who, table.getn(buffer)); local index = 1; while buffer[index] do GAC_SendInventory(who, buffer[index].slot, buffer[index].texture, buffer[index].color, buffer[index].ref, buffer[index].name, buffer[index].count); index = index + 1; end end function GAC_SendRequestInspect(who) SimpleComm_SendMessage( who, { command = GUILDADS_MSG_REQUEST_INSPECT } ); end function GAC_SendRequestOfflines(who) DEBUG_MSG("GAC_SendRequestOfflines("..NoNil(who)..")"); LastSeens[who] = {}; SimpleComm_SendMessage( who, { command = GUILDADS_MSG_REQUEST_OFFLINES } ); end function GAC_SendRequestAds(who, owner) DEBUG_MSG("GAC_SendRequestAds("..NoNil(who)..")"); SimpleComm_SendMessage( who, { command = GUILDADS_MSG_REQUEST_ADS; owner = owner } ); end function GAC_SendAnnonce(who, owner) local powner, level, race, class, name, guildName, guildRankName, guildRankIndex, creationtime; if (owner) then powner = GAS_ProfileGet(owner); level = powner.level; race = powner.race; class = powner.class; name = owner; guildName = powner.guild; creationtime = powner.creationtime; accountid = powner.accountid; else level = UnitLevel("player"); race = GAS_GetRaceId(UnitRace("player")); class = GAS_GetClassId(UnitClass("player")); name = playerName; guildName, guildRankName, guildRankIndex = GetGuildInfo("player"); creationtime = GAS_currentTime(); accountid = GAS_GetAccountId(); end SimpleComm_SendMessage( who, { command = GUILDADS_MSG_ADD; adtype = GUILDADS_MSG_TYPE_ANNONCE; accountId = accountid; class = class; race = race; level = level; guild = guildName; creationtime = creationtime; owner = owner } ); end function GAC_SendRemove(who, adtype, id) SimpleComm_SendMessage( who, { command = GUILDADS_MSG_REMOVE; adtype = adtype; id = id } ); end function GAC_SendRemoveAll(who) SimpleComm_SendMessage( who, { command = GUILDADS_MSG_REMOVE_ALL; } ); end function GAC_SendingUpdate(who, count, owner, delay) SimpleComm_SendMessage( who, { command = GUILDADS_MSG_SENDING_UPDATE, owner = owner, count = count }, delay ); end function GAC_SendingAll(who, owner) SimpleComm_SendMessage( who, { command = GUILDADS_MSG_SENDING_ALL; owner = owner } ); end function GAC_SendingAllEnd(who, owner, delay) local creationtime = 0; if (owner) then creationtime = GAS_ProfileGetUpdatedDate(owner); else creationtime = GAS_ProfileGetUpdatedDate(playerName); end SimpleComm_SendMessage( who, { command = GUILDADS_MSG_SENDING_ALL_END; creationtime = creationtime; owner = owner; }, delay ); end function GAC_SendLastSeen(who, owner, time) DEBUG_MSG("GAC_SendLastSeen("..NoNil(who)..")"); SimpleComm_SendMessage( who, { command = GUILDADS_MSG_LASTSEEN; creationtime = time; owner = owner } ); end function GAC_SendLastSeenEnd(who) DEBUG_MSG("GAC_SendLastSeenEnd("..NoNil(who)..")"); SimpleComm_SendMessage( who, { command = GUILDADS_MSG_LASTSEEN_END; } ); end function GAC_SendAllAds(who, owner, adTable, ad_type, delay) local size = table.getn(adTable); if owner then for i = 1, size, 1 do if adTable[i].owner == owner then GAC_SendAd(who, ad_type, adTable[i], delay); end end else for i = 1, size, 1 do GAC_SendAd(who, ad_type, adTable[i], delay); end end end function GAC_SendAllAdsType(who, owner, delay) DEBUG_MSG("GAC_SendAllAdsType("..NoNil(who)..","..NoNil(owner)..")"); -- On n'envois pas à who ses annonces -- sauf si c'est nous même, ou si c'est à tout le monde if (who == owner) and (who ~= playerName) and (who ~= nil) then return; end -- Ads->Start GAC_SendingAll(who, owner); -- Ads : Guild GAC_SendAnnonce(who, owner); -- Ads : Ask/Have/Event local adTables = {}; if (owner) then adTables = GAS_GetAds(); else adTables = GAS_GetMyAds(); end GAC_SendAllAds(who, owner, adTables[GUILDADS_MSG_TYPE_REQUEST], GUILDADS_MSG_TYPE_REQUEST, delay); GAC_SendAllAds(who, owner, adTables[GUILDADS_MSG_TYPE_AVAILABLE], GUILDADS_MSG_TYPE_AVAILABLE, delay); GAC_SendAllAds(who, owner, adTables[GUILDADS_MSG_TYPE_EVENT], GUILDADS_MSG_TYPE_EVENT, delay); -- Ads : Skills if owner then GAC_SendAllAds(who, owner, adTables[GUILDADS_MSG_TYPE_SKILL], GUILDADS_MSG_TYPE_SKILL, delay); else GAC_SendSkills(who, delay); end -- Ads->End GAC_SendingAllEnd(who, owner, delay); end function GAC_SendMeta(who) SimpleComm_SendMessage( who, { command = GUILDADS_MSG_META; text = GUILDADS_VERSION; creationtime = StartTime; id = MyState; } ); end function GAC_SendChatFlag(who) local flag, message = SimpleComm_GetFlag(playerName); SimpleComm_SendMessage( nil, { command = GUILDADS_MSG_CHATFLAG; flag = flag; text = message; } ); end -------------------------------------------------------------------------------- -- -- OnMessage -- --------------------------------------------------------------------------------- function GAC_OnMessage(author, message) if (GUILDADSCOMM_DEBUG) then DEBUG_MSG("[OnMessage,"..author.."]: "..GuildAds_Serialize(message)); end -- Ignore this author ? if GAS_ProfileIsIgnored(author) then return; end -- Set online GAS_SetOnlineStatus(author, true); -- A propos de quelle personne : l'auteur du message (author) ou une autre (message.owner) local owner = author; if (message.owner) then owner = message.owner; end -- Mise à jour du dernier message de author if (owner==author) and ((message.command == GUILDADS_MSG_ADD) or (message.command == GUILDADS_MSG_REMOVE)) then GAS_ProfileSetUpdatedDate(author, message.creationtime); end -- -- Process all the messages we know about -- if (message.command == GUILDADS_MSG_ADD) then -- Accept only if : -- this owner isn't ignored -- AND ( this owner has sent GUILDADS_MSG_SENDING_ALL which was accepted. -- OR this owner has sent a GUILDADS_MSG_SENDING_UPDATE ) if not GAS_ProfileIsIgnored(owner) and (MonitorAds[owner] or WatingForUpdate[owner]) then -- local adtype = message.adtype; -- Use this update if WatingForUpdate[owner] then WatingForUpdate[owner] = WatingForUpdate[owner] - 1; if WatingForUpdate[owner]==0 then WatingForUpdate[owner] = nil; end DEBUG_MSG("Count"..NoNil(WatingForUpdate[owner])); end -- Call onMessageAd if OnMessageAd[adtype] then if not OnMessageAd[adtype](author, message) then return; end end -- Inventory if (adtype == GUILDADS_MSG_TYPE_INVENTORY) then GAS_ProfileSetInventorySlot(message.creationtime, owner, message.id, message.itemColor, message.itemRef, message.itemName, message.texture, message.count); return false; end -- Mise à jour du profile if (adtype == GUILDADS_MSG_TYPE_ANNONCE) then GAS_ProfileSetGeneral(message.creationtime, owner, message.race, message.class, message.level, message.guild); GAS_ProfileSetAccountId(owner, message.accountId); message.guild = nil; message.level = nil; message.class = nil; message.race = nil; message.accountId = nil; message.id = owner; end -- Monitor : l'annonce a été mise à jour -- cf GUILDADS_MSG_SENDING_ALL et GUILDADS_MSG_SENDING_ALL_END if adtype and owner and MonitorAds[owner] then if (MonitorAds[owner][adtype] == nil) then MonitorAds[owner][adtype] = {}; end MonitorAds[owner][adtype][message.id] = true; end -- Scan for dupes if ((adtype == GUILDADS_MSG_TYPE_REQUEST) or (adtype == GUILDADS_MSG_TYPE_AVAILABLE) or (adtype == GUILDADS_MSG_TYPE_EVENT)) then local ads = GAS_GetAds(); for k,v in ads[adtype] do if ( v.owner == owner and v.id == message.id and v.text == message.text and v.itemName == message.itemName and v.count == message.count ) then -- DONT ADD IT AGAIN DEBUG_MSG("GuildAds_OnMessage: Already in DB"); return false; end end end -- Effacer le précédent Id GAS_RemoveByOwnerAndId(owner, adtype, message.id); -- Efface les donnees inutiles message.adtype = nil; message.command = nil; message.owner = owner; message.currenttime = nil; -- Traduction du nom si necessaire if message.itemName and message.itemRef then local info = GAS_GetItemInfo(message.itemRef); if (info.name) then message.itemName = info.name; end end -- Ajout de l'annonce GAS_AddAd(owner, adtype, message); end elseif (message.command == GUILDADS_MSG_REMOVE) then -- Ignore this owner ? if not GAS_ProfileIsIgnored(owner) then -- A previously placed add is being removed GAS_RemoveByOwnerAndId(owner, message.adtype, message.id); end elseif (message.command == GUILDADS_MSG_REQUEST_INSPECT) then if not GAS_ProfileIsIgnored(owner) then -- Send inspect to the author -- Always my inventory GAC_SendInspect(author); end elseif (message.command == GUILDADS_MSG_REQUEST_OFFLINES) then if not GAS_ProfileIsIgnored(owner) then -- Pour chaque personne offline -- -> envois la date du dernier message local ads = GAS_GetAds(); for i, ad in ads[GUILDADS_MSG_TYPE_ANNONCE] do if (not GAS_IsOnline(ad.owner)) and (not GAS_ProfileIsIgnored(ad.owner)) then GAC_SendLastSeen(author, ad.owner, GAS_ProfileGetUpdatedDate(ad.owner)); end end GAC_SendLastSeenEnd(author); end elseif (message.command == GUILDADS_MSG_LASTSEEN) then if (owner ~= playerName) and (LastSeens[author]) and (not GAS_ProfileIsIgnored(owner)) then LastSeens[author][owner] = true; local myUpdate = GAS_ProfileGetUpdatedDate(owner); if (message.creationtime ~= myUpdate) then if (myUpdate) then if (message.creationtime==nil or message.creationtime < myUpdate) then DEBUG_MSG("LastSeen("..owner..")="..NoNil(message.creationtime).."<"..NoNil(myUpdate)); -- on doit faire la mise à jour pour tout le monde GAC_SendAllAdsType(nil, owner); elseif (message.creationtime > myUpdate) then DEBUG_MSG("LastSeen("..owner..")="..NoNil(message.creationtime)..">"..NoNil(myUpdate)); -- on doit récupérer la mise à jour tinsert(WaitingOfflinesAds, owner); end else DEBUG_MSG("LastSeen("..owner..")="..NoNil(message.creationtime).."/"..NoNil(myUpdate)); -- on doit récupérer la mise à jour tinsert(WaitingOfflinesAds, owner); end end end elseif (message.command == GUILDADS_MSG_LASTSEEN_END) then if LastSeens[author] and not GAS_ProfileIsIgnored(author) then -- envois les annonces des joueurs non connus. local ads = GAS_GetAds(); for i, ad in ads[GUILDADS_MSG_TYPE_ANNONCE] do -- si ad.owner est offline et non connu par author -- alors on envois ses informations à propos de ad.owner if (not GAS_IsOnline(ad.owner) and LastSeens[author][ad.owner]==nil) then GAC_SendAllAdsType(nil, ad.owner); end end -- Dernier message de author à propos des offlines -- Donc on n'écoute plus les message de author LastSeens[author] = nil; -- if (table.getn(WaitingOfflinesAds) > 0) then -- demande de mise à jour pour soi. for _, owner in WaitingOfflinesAds do GAC_SendRequestAds(author, owner); end else -- aucune demande de offlines -- on est donc synchro : passage a l'etat OK GuildAdsSystem.SynchronizeOfflinesTimer = nil; MyState = GUILDADS_STATE_OK; GAC_SendMeta(nil); end end elseif (message.command == GUILDADS_MSG_REQUEST_ADS) then -- Someone is requesting ads, probably just arrived in the channel -- if message.owner is set : owner's ads -- if message.owner is nil : my ads (send my ads in few seconds) -- owner is meaningless if GuildAds.Config.PublishMyAds and not GAS_ProfileIsIgnored(owner) then if (author ~= playerName) then if message.owner then GAC_SendAllAdsType(author, message.owner); else GAC_SendMeta(author); GAC_SendChatFlag(author); GAC_SendAllAdsType(author, nil, math.random(20)); end end end elseif (message.command == GUILDADS_MSG_SENDING_UPDATE) then WatingForUpdate[owner] = (WatingForUpdate[owner] or 0) + message.count; elseif (message.command == GUILDADS_MSG_SENDING_ALL) then if not GAS_ProfileIsIgnored(owner) then -- Start recording each update ads if message.owner then -- Accept offline ads from owner we need. (see GUILDADS_MSG_LASTSEEN) i = 1; while WaitingOfflinesAds[i] and WaitingOfflinesAds[i]~=owner do i = i+1; end if WaitingOfflinesAds[i] and WaitingOfflinesAds[i]==owner then MonitorAds[owner] = {}; end -- accept new ads from author who are in GUILDADS_STATE_SYNC_OFFLINE state. if MetaPlayers[author] and MetaPlayers[author].state==GUILDADS_STATE_SYNC_OFFLINE then MonitorAds[owner] = {}; end else -- online synchronization : ok MonitorAds[owner] = {}; end end elseif (message.command == GUILDADS_MSG_SENDING_ALL_END) then if MonitorAds[owner] and not GAS_ProfileIsIgnored(owner) then -- Update profile date if owner then GAS_ProfileSetUpdatedDate(owner, message.creationtime); end -- for each owner's ads -- if not updated (ie in MonitorAds), we delete it local ads = GAS_GetAds(); for adtype, tads in ads do if (MonitorAds[owner][adtype] == nil) then GAS_RemoveByOwner(owner, adtype); else for i, ad in tads do if (owner==ad.owner and MonitorAds[owner][adtype][ad.id] == nil) then GAS_RemoveByOwnerAndId(owner, adtype, ad.id); end end end end -- stop monitoring MonitorAds[owner] = nil; -- Unstack owner in WatingOfflineAds -- if this is the last, we are sync -- so set state to GUILDADS_STATE_OK i = 1; while WaitingOfflinesAds[i] and WaitingOfflinesAds[i]~=owner do i = i+1; end if WaitingOfflinesAds[i] and WaitingOfflinesAds[i]==owner then table.remove(WaitingOfflinesAds, i); if (table.getn(WaitingOfflinesAds)==0) then GuildAdsSystem.SynchronizeOfflinesTimer = nil; MyState = GUILDADS_STATE_OK; GAC_SendMeta(nil); end end end elseif (message.command == GUILDADS_MSG_REMOVE_ALL) then if not GAS_ProfileIsIgnored(owner) then -- Remove all the ads from the owner GAS_RemoveByOwner(owner, GUILDADS_MSG_TYPE_ANNONCE); GAS_RemoveByOwner(owner, GUILDADS_MSG_TYPE_REQUEST); GAS_RemoveByOwner(owner, GUILDADS_MSG_TYPE_AVAILABLE); GAS_RemoveByOwner(owner, GUILDADS_MSG_TYPE_SKILL); GAS_RemoveByOwner(owner, GUILDADS_MSG_TYPE_EVENT); end elseif (message.command == GUILDADS_MSG_META) then if not GAS_ProfileIsIgnored(author) then MetaPlayers[author] = { state = message.id; onlineSince = message.creationtime; version = message.text; }; end elseif (message.command == GUILDADS_MSG_CHATFLAG) then if author~=playerName then SimpleComm_SetFlag(author, message.flag, message.text); end elseif OnMessageCommand[message.command] then OnMessageCommand[message.command](author, message); else -- This message was unknown end end -------------------------------------------------------------------------------- -- -- Serialize/Unserialize -- --------------------------------------------------------------------------------- function SerializeId(obj) if (type(obj) == "nil" ) then return ""; else return obj end end function UnserializeId(str) return str; end function SerializeString(obj) if (type(obj) == "nil" ) then return ""; else return obj end end function UnserializeString(str) if (str == "") then return nil; else return str end end function SerializeTexture(obj) if (type(obj) == "nil" ) then return ""; else return string.gsub(obj, "Interface\\Icons\\", "\@"); end end function UnserializeTexture(str) if (str == "") then return nil; else return string.gsub(str, "\@", "Interface\\Icons\\"); end end function SerializeItemRef(obj) if (type(obj) == "nil" ) then return ""; else return string.gsub(string.gsub(obj, "item\:", "\@"), ":0:0:0", "\*"); end end function UnserializeItemRef(str) if (str == "") then return nil; else return string.gsub(string.gsub(str, "\@", "item\:"), "\*", ":0:0:0"); end end function SerializeColor(obj) if obj==nil then return ""; elseif obj=="ffa335ee" then return "E"; -- epic elseif obj=="ff0070dd" then return "R"; -- rare elseif obj=="ff1eff00" then return "U" -- uncommun elseif obj=="ffffffff" then return "C" -- common elseif obj=="ff9d9d9d" then return "P" -- poor else return obj; end end function UnserializeColor(str) if str==nil then return nil elseif str=="E" then return "ffa335ee"; elseif str=="R" then return "ff0070dd"; elseif str=="U" then return "ff1eff00"; elseif str=="C" then return "ffffffff"; elseif str=="P" then return "ff9d9d9d"; else return str; end end function SerializeInteger(obj) if (type(obj) == "nil" ) then return ""; else return obj; end end function UnserializeInteger(str) return tonumber(str); end function SerializeTime(obj) if (type(obj) == "nil" ) then return ""; else -- convertion en base 52 value = ""; while (obj ~= 0) do i = floor(obj / 52); j = obj - i*52; if (j>=26) then value = string.char(65+j-26)..value; else value = string.char(96+j)..value; end obj = i; end return value; end end function UnserializeTime(str) if (str == "") then return nil; else number = 0; for i=1, string.len(str),1 do o = string.byte(str, i); if (o> 95) then j = o-96; else j = o-65+26; end number = number*52 + j; end return number; end end function SerializeObj(obj) if (type(obj) == "nil" ) then return ""; elseif ( type(obj) == "string" ) then return "s"..string.gsub(string.gsub(string.gsub(obj, ">", ">"), "|([cHhr])", "&%1;"), "|", "&p;"); elseif ( type(obj) == "number" ) then return "n"..obj; elseif ( type(obj) == "boolean" ) then if (value) then return "1"; else return "0"; end elseif ( type(obj) == "function" ) then return ""; -- nil elseif ( type(obj) == "table" ) then return ""; -- nil end return ""; end function UnserializeObj(str) if (str == "") then return nil; else typeString = string.sub(str, 0, 1); valueString = string.sub(str, 2); if (typeString == "s") then return string.gsub(string.gsub(string.gsub(valueString, ">", ">"), "&(%w);", "|%1"), "&p;", "|"); elseif (typeString == "n") then return tonumber(valueString); elseif (typeString == "1") then return true; elseif (typeString == "0") then return false; else DEBUG_MSG("GuildAds_Unserialize: Type non reconnu:"..str); return nil; end end end local SerializeMeta = { [1] = { ["key"] ="command", ["fout"]=SerializeId, ["fin"]=UnserializeId } }; local SerializeCommand = { [GUILDADS_MSG_ADD] = { [1] = { ["key"]="owner", ["fout"]=SerializeString, ["fin"]=UnserializeString }, [2] = { ["key"]="adtype", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [3] = { ["key"]="id", ["fout"]=SerializeObj, ["fin"]=UnserializeObj }, [4] = { ["key"]="currenttime", ["fout"]=SerializeTime, ["fin"]=UnserializeTime}, [5] = { ["key"]="creationtime", ["fout"]=SerializeTime, ["fin"]=UnserializeTime}, }, [GUILDADS_MSG_REMOVE] = { [1] = { ["key"] ="owner", ["fout"]=SerializeString, ["fin"]=UnserializeString }, [2] = { ["key"] ="adtype", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [3] = { ["key"] ="id", ["fout"]=SerializeObj, ["fin"]=UnserializeObj }, }, [GUILDADS_MSG_REMOVE_ALL] = { [1] = { ["key"] ="owner", ["fout"]=SerializeString, ["fin"]=UnserializeString }, }, [GUILDADS_MSG_REQUEST_ADS] = { [1] = { ["key"] ="owner", ["fout"]=SerializeString, ["fin"]=UnserializeString }, }, [GUILDADS_MSG_REQUEST_OFFLINES] = {}, [GUILDADS_MSG_REQUEST_INSPECT] = {}, [GUILDADS_MSG_SENDING_UPDATE] = { [1] = { ["key"] ="owner", ["fout"]=SerializeString, ["fin"]=UnserializeString }, [2] = { ["key"] ="count", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, }, [GUILDADS_MSG_SENDING_ALL] = { [1] = { ["key"] ="owner", ["fout"]=SerializeString, ["fin"]=UnserializeString }, }, [GUILDADS_MSG_SENDING_ALL_END] = { -- currenttime, profiletime [1] = { ["key"] ="owner", ["fout"]=SerializeString, ["fin"]=UnserializeString }, [2] = { ["key"] ="currenttime", ["fout"]=SerializeTime, ["fin"]=UnserializeTime}, [3] = { ["key"] ="creationtime", ["fout"]=SerializeTime, ["fin"]=UnserializeTime }, }, [GUILDADS_MSG_LASTSEEN] = { -- currenttime, profiletime [1] = { ["key"] ="owner", ["fout"]=SerializeString, ["fin"]=UnserializeString }, [2] = { ["key"] ="currenttime", ["fout"]=SerializeTime, ["fin"]=UnserializeTime}, [3] = { ["key"] ="creationtime", ["fout"]=SerializeTime, ["fin"]=UnserializeTime }, }, [GUILDADS_MSG_LASTSEEN_END] = {}, [GUILDADS_MSG_META] = { -- currenttime, version, starttime, id [1] = { ["key"] ="currenttime", ["fout"]=SerializeTime, ["fin"]=UnserializeTime}, [2] = { ["key"] ="text", ["fout"]=SerializeString, ["fin"]=UnserializeString }, [3] = { ["key"] ="creationtime", ["fout"]=SerializeTime, ["fin"]=UnserializeTime }, [4] = { ["key"] ="id", ["fout"]=SerializeId, ["fin"]=UnserializeId }, }, [GUILDADS_MSG_CHATFLAG] = { -- flag, text [1] = { ["key"] ="flag", ["fout"]=SerializeString, ["fin"]=UnserializeString}, [2] = { ["key"] ="text", ["fout"]=SerializeString, ["fin"]=UnserializeString }, }, }; local SerializeAd = { [GUILDADS_MSG_TYPE_ANNONCE] = { -- accountId, class, race, level, guild [1] = { ["key"] ="accountId", ["fout"]=SerializeObj, ["fin"]=UnserializeObj }, [2] = { ["key"] ="class", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [3] = { ["key"] ="race", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [4] = { ["key"] ="level", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [5] = { ["key"] ="guild", ["fout"]=SerializeString, ["fin"]=UnserializeString }, }, [GUILDADS_MSG_TYPE_INVENTORY] = { -- itemRef, itemName, itemColor, count, texture [1] = { ["key"] ="itemColor", ["fout"]=SerializeColor, ["fin"]=UnserializeColor }, [2] = { ["key"] ="itemRef", ["fout"]=SerializeItemRef, ["fin"]=UnserializeItemRef }, [3] = { ["key"] ="itemName", ["fout"]=SerializeString, ["fin"]=UnserializeString }, [4] = { ["key"] ="count", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [5] = { ["key"] ="texture", ["fout"]=SerializeTexture, ["fin"]=UnserializeTexture }, }, [GUILDADS_MSG_TYPE_SKILL] = { -- skillRank , skillMaxRank [1] = { ["key"] ="skillRank", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [2] = { ["key"] ="skillMaxRank", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, }, [GUILDADS_MSG_TYPE_REQUEST] = { -- text, itemRef, itemName, itemColor, count, texture [1] = { ["key"] ="text", ["fout"]=SerializeObj, ["fin"]=UnserializeObj }, [2] = { ["key"] ="itemColor", ["fout"]=SerializeColor, ["fin"]=UnserializeColor }, [3] = { ["key"] ="itemRef", ["fout"]=SerializeItemRef, ["fin"]=UnserializeItemRef }, [4] = { ["key"] ="itemName", ["fout"]=SerializeString, ["fin"]=UnserializeString }, [5] = { ["key"] ="count", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [6] = { ["key"] ="texture", ["fout"]=SerializeTexture, ["fin"]=UnserializeTexture }, }, [GUILDADS_MSG_TYPE_AVAILABLE] ={ -- text, itemRef, itemName, itemColor, count, texture [1] = { ["key"] ="text", ["fout"]=SerializeObj, ["fin"]=UnserializeObj }, [2] = { ["key"] ="itemColor", ["fout"]=SerializeColor, ["fin"]=UnserializeColor }, [3] = { ["key"] ="itemRef", ["fout"]=SerializeItemRef, ["fin"]=UnserializeItemRef }, [4] = { ["key"] ="itemName", ["fout"]=SerializeString, ["fin"]=UnserializeString }, [5] = { ["key"] ="count", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [6] = { ["key"] ="texture", ["fout"]=SerializeTexture, ["fin"]=UnserializeTexture }, }, [GUILDADS_MSG_TYPE_EVENT] = { -- text [1] = { ["key"] ="text", ["fout"]=SerializeObj, ["fin"]=UnserializeObj }, [2] = { ["key"] ="eventtime", ["fout"]=SerializeTime, ["fin"]=UnserializeTime }, [3] = { ["key"] ="note", ["fout"]=SerializeString, ["fin"]=UnserializeString }, [4] = { ["key"] ="count", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [5] = { ["key"] ="minlevel", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, [6] = { ["key"] ="maxlevel", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, }, [GUILDADS_MSG_TYPE_EVENT_SUBSCRIPTION] = { [1] = { ["key"] ="eventid", ["fout"]=SerializeInteger, ["fin"]=UnserializeInteger }, } }; function SerializeTable(spec, obj) local tmp = ""; local index = table.getn(spec); while (index > 0) do local info = spec[index]; local result = info.fout(obj[info.key]); if (tmp ~= "") or (result ~= "") then tmp = result..">"..tmp; end; index = index -1; end return tmp; end function GuildAds_SerializeCommand(obj) local result = SerializeTable(SerializeMeta, obj) if SerializeCommand[obj.command] then result = result .. SerializeTable(SerializeCommand[obj.command], obj); if obj.command == GUILDADS_MSG_ADD then result = result .. SerializeTable(SerializeAd[obj.adtype], obj); end end return result; end function GuildAds_UnserializeCommand(text) local obj= { }; local spec=SerializeMeta; local index=1; local size= table.getn(spec); local step=1; for str in string.gfind(text, "([^\>]*)>") do info = spec[index]; obj[info.key] = info.fin(str); index = index + 1; if index>size then step=step+1; if step==2 then if obj.command and SerializeCommand[obj.command] then spec=SerializeCommand[obj.command]; else spec=nil; end elseif step==3 then if obj.adtype and SerializeAd[obj.adtype] then spec=SerializeAd[obj.adtype]; else spec=nil; end else return obj; end if spec then index=1; size=table.getn(spec); else return obj end end end return obj; end function GuildAds_Serialize(obj) obj["currenttime"] = GAS_currentTime(); return GUILDADS_MSG_PREFIX..GuildAds_SerializeCommand(obj); end function GuildAds_Unserialize(str) local iStart, iEnd, msg, params; iStart, iEnd, params = string.find(str, GUILDADS_MSG_PREFIX.."(.*)"); if (iStart) then return GuildAds_UnserializeCommand(params); else iStart, iEnd, params = string.find(str, GUILDADS_MSG_PREFIX_CHATCOMM.."(.*)"); if (iStart) then return GuildAds_UnserializeCommand(params); else return nil end end end function GuildAds_FilterText(text) return string.sub(text, 1, string.len(GUILDADS_MSG_PREFIX_NOVERSION)) == GUILDADS_MSG_PREFIX_NOVERSION or string.sub(text, 1, string.len(GUILDADS_MSG_PREFIX_TOKEN)) == GUILDADS_MSG_PREFIX_TOKEN; end -------------------------------------------------------------------------------- -- -- Register/Unregister new command -- --------------------------------------------------------------------------------- function GAC_RegisterCommand(command, serializeInfo, onMessage) if SerializeCommand[command] then return false, "command("..command..") already registered"; else if type(onMessage) == "function" then if type(serializeInfo) == "table" then for _, spec in serializeInfo do if type(spec.key)~="string" or type(spec.fin)~="function" or type(spec.fout)~="function" then if type(spec.key)=="string" then return false, "serializeInfo["..spec.key.."]="..type(spec.fin)..","..type(spec.fout); else return false, "serializeInfo[??]="..type(spec.fin)..","..type(spec.fout); end end end SerializeCommand[command] = serializeInfo; OnMessageCommand[command] = onMessage; return true; else return false, "type(serializeInfo)="..type(serializeInfo); end else return false, "type(onMessage)="..type(onMessage); end end end function GAC_UnregisterCommand(command) SerializeCommand[command] = nil; OnMessageCommand[command] = nil; end function GAC_IsRegisteredCommand(command) if SerializeCommand[command] then return true, SerializeCommand[command], OnMessageCommand[command]; else return false; end end -------------------------------------------------------------------------------- -- -- Register/Unregister new ad type -- --------------------------------------------------------------------------------- function GAC_RegisterAdtype(adtype, serializeInfo, onMessage) if SerializeAd[adtype] then return false, "adtype("..command..") already registered"; else if type(onMessage) == "function" then if type(serializeInfo) == "table" then for _, spec in serializeInfo do if type(spec.key)~="string" or type(spec.fin)~="function" or type(spec.fout)~="function" then return false, "serializeInfo["..spec.key.."]="..type(spec.fin)..","..type(spec.fout); end end SerializeAd[adtype] = serializeInfo; OnMessageAd[adtype] = onMessage; return true; else return false, "type(serializeInfo)="..type(serializeInfo); end else return false, "type(onMessage)="..type(onMessage); end end end function GAC_UnregisterAdtype(adtype) SerializeAd[adtype] = nil; OnMessageAd[adtype] = nil; end function GAC_IsRegisteredAdtype(adtype) if SerializeCommand[adtype] then return true, SerializeAd[adtype], OnMessageAd[adtype]; else return false; end end -- export GAC_SerializeObj = SerializeObj; GAC_UnserializeObj = UnserializeObj; GAC_SerializeString = SerializeString; GAC_UnserializeString = UnserializeString; GAC_SerializeInteger = SerializeInteger; GAC_UnserializeInteger = UnserializeInteger; GAC_SerializeColor = SerializeColor; GAC_UnserializeColor = UnserializeColor;