-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add more advanced broadcasting logic
- Loading branch information
1 parent
533ee07
commit d59f53c
Showing
9 changed files
with
295 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
-- Addon global | ||
local TheClassicRace = _G.TheClassicRace | ||
|
||
-- WoW API | ||
local C_Timer, IsInGuild = _G.C_Timer, _G.IsInGuild | ||
|
||
--[[ | ||
TheClassicRaceBroadcasterdoes a left-most binary search | ||
to find the lower bound level in our /who query which gives > 0 results but < 50 (because we only get 50 from 1 query) | ||
]]-- | ||
---@class TheClassicRaceBroadcaster | ||
---@field Config TheClassicRaceConfig | ||
---@field Core TheClassicRaceCore | ||
---@field DB table<string, table> | ||
---@field EventBus TheClassicRaceEventBus | ||
local TheClassicRaceBroadcaster = {} | ||
TheClassicRaceBroadcaster.__index = TheClassicRaceBroadcaster | ||
TheClassicRace.Broadcaster = TheClassicRaceBroadcaster | ||
setmetatable(TheClassicRaceBroadcaster, { | ||
__call = function(cls, ...) | ||
return cls.new(...) | ||
end, | ||
}) | ||
|
||
function TheClassicRaceBroadcaster.new(Config, Core, DB, Network) | ||
local self = setmetatable({}, TheClassicRaceBroadcaster) | ||
|
||
self.Config = Config | ||
self.Core = Core | ||
self.DB = DB | ||
self.Network = Network | ||
|
||
self.timer = nil | ||
self.ticker = nil | ||
self.done = false | ||
|
||
return self | ||
end | ||
|
||
function TheClassicRaceBroadcaster:IsDone() | ||
return self.done | ||
end | ||
|
||
function TheClassicRaceBroadcaster:Start() | ||
if self.timer ~= nil or self.ticker ~= nil then | ||
return | ||
end | ||
|
||
-- to avoid everyone else who also received RequestUpdate from being in sync with us | ||
-- we'll add a random sleep offset | ||
-- @TODO: maybe we can do something better? | ||
-- like attempt to observe other broadcasters who are in sync with us offset to cover that | ||
local randomOffset = math.random(1, 10) | ||
|
||
self.timer = C_Timer.NewTimer(randomOffset, function() | ||
self.ticker = C_Timer.NewTicker(10, function() | ||
self:Broadcast() | ||
end) | ||
end) | ||
end | ||
|
||
function TheClassicRaceBroadcaster:Broadcast() | ||
-- iterate over leaderboard descending | ||
for _, playerInfo in ipairs(self.DB.realm.leaderboard) do | ||
-- if a player hasn't been "observed" yet since our last received RequestUpdate | ||
-- then we can broadcast his info | ||
if playerInfo.observedAt < self.DB.realm.lastRequestUpdate then | ||
self.Network:SendObject(self.Config.Network.Events.PlayerInfo, | ||
{ playerInfo.name, playerInfo.level, playerInfo.dingedAt }, "CHANNEL") | ||
if IsInGuild() then | ||
self.Network:SendObject(self.Config.Network.Events.PlayerInfo, | ||
{ playerInfo.name, playerInfo.level, playerInfo.dingedAt }, "GUILD") | ||
end | ||
|
||
-- update observedAt | ||
playerInfo.observedAt = self.Core:Now() | ||
|
||
-- return out of this function completely | ||
return | ||
end | ||
end | ||
|
||
-- all players on our leaderboard were "observed" since our last received RequestUpdate | ||
-- this means we can stop broadcasting | ||
self.ticker:Cancel() | ||
self.done = true | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
-- load test base | ||
local TheClassicRace = require("testbase") | ||
|
||
-- WoW API stubs | ||
local SetIsInGuild, C_Timer = _G.SetIsInGuild, _G.C_Timer | ||
|
||
describe("Broadcaster", function() | ||
---@type TheClassicRaceConfig | ||
local config | ||
local db | ||
---@type TheClassicRaceCore | ||
local core | ||
---@type TheClassicRaceNetwork | ||
local network | ||
---@type TheClassicRaceBroadcaster | ||
local broadcaster | ||
local time = 1000000000 | ||
|
||
before_each(function() | ||
SetIsInGuild(false) | ||
|
||
config = TheClassicRace.Config | ||
db = LibStub("AceDB-3.0"):New("TheClassicRace_DB", TheClassicRace.DefaultDB, true) | ||
db:ResetDB() | ||
core = TheClassicRace.Core("Nub", "NubVille") | ||
-- mock core:Now() to return our mocked time | ||
function core:Now() return time end | ||
network = {SendObject = function() end} | ||
broadcaster = TheClassicRace.Broadcaster(config, core, db, network) | ||
broadcaster.ticker = C_Timer.NewTicker() | ||
end) | ||
|
||
after_each(function() | ||
SetIsInGuild(nil) | ||
end) | ||
|
||
it("basic broadcast", function() | ||
local networkSpy = spy.on(network, "SendObject") | ||
|
||
-- insert some data into leaderboard | ||
db.realm.leaderboard = { | ||
{ name = "Leader", level = 13, observedAt = time - 1 }, | ||
{ name = "Nub1", level = 13, observedAt = time - 1 }, | ||
} | ||
|
||
-- set lastRequestUpdate | ||
db.realm.lastRequestUpdate = time | ||
|
||
broadcaster:Broadcast() | ||
|
||
assert.spy(networkSpy).was_called_with(match.is_ref(network), config.Network.Events.PlayerInfo, | ||
match.is_same({ db.realm.leaderboard[1].name, db.realm.leaderboard[1].level, nil }), "CHANNEL") | ||
|
||
broadcaster:Broadcast() | ||
|
||
assert.spy(networkSpy).was_called_with(match.is_ref(network), config.Network.Events.PlayerInfo, | ||
match.is_same({ db.realm.leaderboard[2].name, db.realm.leaderboard[2].level, nil }), "CHANNEL") | ||
|
||
broadcaster:Broadcast() | ||
|
||
assert.spy(networkSpy).called_at_most(2) | ||
assert.equals(true, broadcaster:IsDone()) | ||
end) | ||
|
||
it("external observed, won't broadcast", function() | ||
local networkSpy = spy.on(network, "SendObject") | ||
|
||
-- insert some data into leaderboard | ||
db.realm.leaderboard = { | ||
{ name = "Leader", level = 13, observedAt = time - 1 }, | ||
{ name = "Nub1", level = 13, observedAt = time - 1 }, | ||
} | ||
|
||
-- set lastRequestUpdate | ||
db.realm.lastRequestUpdate = time | ||
|
||
broadcaster:Broadcast() | ||
|
||
assert.spy(networkSpy).was_called_with(match.is_ref(network), config.Network.Events.PlayerInfo, | ||
match.is_same({ db.realm.leaderboard[1].name, db.realm.leaderboard[1].level, nil }), "CHANNEL") | ||
|
||
-- something external caused observedAt to be bumped for player 2 | ||
db.realm.leaderboard[2].observedAt = time | ||
|
||
broadcaster:Broadcast() | ||
|
||
assert.spy(networkSpy).called_at_most(2) | ||
assert.equals(true, broadcaster:IsDone()) | ||
end) | ||
|
||
it("request update mid broadcast", function() | ||
local networkSpy = spy.on(network, "SendObject") | ||
|
||
-- insert some data into leaderboard | ||
db.realm.leaderboard = { | ||
{ name = "Leader", level = 13, observedAt = time - 1 }, | ||
{ name = "Nub1", level = 13, observedAt = time - 1 }, | ||
} | ||
|
||
-- set lastRequestUpdate | ||
db.realm.lastRequestUpdate = time | ||
|
||
broadcaster:Broadcast() | ||
|
||
assert.spy(networkSpy).was_called_with(match.is_ref(network), config.Network.Events.PlayerInfo, | ||
match.is_same({ db.realm.leaderboard[1].name, db.realm.leaderboard[1].level, nil }), "CHANNEL") | ||
|
||
-- something external caused observedAt to be bumped for player 2 | ||
-- which would cause the broadcast to be done | ||
db.realm.leaderboard[2].observedAt = time | ||
|
||
-- but lastRequestUpdate is also bumped, so we restart the broadcasting sequence from first player | ||
time = time + 1 | ||
db.realm.lastRequestUpdate = time | ||
|
||
broadcaster:Broadcast() | ||
|
||
assert.spy(networkSpy).was_called_with(match.is_ref(network), config.Network.Events.PlayerInfo, | ||
match.is_same({ db.realm.leaderboard[1].name, db.realm.leaderboard[1].level, nil }), "CHANNEL") | ||
|
||
broadcaster:Broadcast() | ||
|
||
assert.spy(networkSpy).was_called_with(match.is_ref(network), config.Network.Events.PlayerInfo, | ||
match.is_same({ db.realm.leaderboard[2].name, db.realm.leaderboard[2].level, nil }), "CHANNEL") | ||
|
||
broadcaster:Broadcast() | ||
|
||
assert.spy(networkSpy).called_at_most(3) | ||
assert.equals(true, broadcaster:IsDone()) | ||
end) | ||
end) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.