-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use new friendtracker (Wesmania) to signal friend-game events add player-info (and runtime-info on client start) stash signals on client start until chatter is added
- Loading branch information
Showing
4 changed files
with
227 additions
and
58 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
from PyQt5.QtCore import QObject, pyqtSignal | ||
from enum import Enum | ||
from model.game import GameState | ||
|
||
class FriendEvents(Enum): | ||
HOSTING_GAME = 1 | ||
JOINED_GAME = 2 | ||
REPLAY_AVAILABLE = 3 | ||
|
||
|
||
class OnlineFriendsTracker(QObject): | ||
""" | ||
Keeps track of current online friends. Notifies about added or removed | ||
friends, no matter if it happens through (dis)connecting or through | ||
the user adding or removing friends. | ||
""" | ||
friendAdded = pyqtSignal(object) | ||
friendRemoved = pyqtSignal(object) | ||
|
||
def __init__(self, me, playerset): | ||
QObject.__init__(self) | ||
self.friends = set() | ||
self._me = me | ||
self._playerset = playerset | ||
|
||
self._me.relationsUpdated.connect(self._update_friends) | ||
self._playerset.playerAdded.connect(self._add_or_update_player) | ||
self._playerset.playerRemoved.connect(self._remove_player) | ||
|
||
for player in self._playerset: | ||
self._add_or_update_player(player) | ||
|
||
def _is_friend(self, player): | ||
return self._me.isFriend(player.id) | ||
|
||
def _add_friend(self, player): | ||
if player in self.friends: | ||
return | ||
self.friends.add(player) | ||
self.friendAdded.emit(player) | ||
|
||
def _remove_friend(self, player): | ||
if player not in self.friends: | ||
return | ||
self.friends.remove(player) | ||
self.friendRemoved.emit(player) | ||
|
||
def _add_or_update_player(self, player): | ||
if self._is_friend(player): | ||
self._add_friend(player) | ||
else: | ||
self._remove_friend(player) | ||
|
||
def _remove_player(self, player): | ||
self._remove_friend(player) | ||
|
||
def _update_friends(self, player_ids): | ||
for pid in player_ids: | ||
try: | ||
player = self._playerset[pid] | ||
except KeyError: | ||
continue | ||
self._add_or_update_player(player) | ||
|
||
|
||
class FriendEventTracker(QObject): | ||
""" | ||
Tracks and notifies about interesting events of a single friend player. | ||
""" | ||
friendEvent = pyqtSignal(object, object) | ||
|
||
def __init__(self, friend): | ||
QObject.__init__(self) | ||
self._friend = friend | ||
self._friend_game = None | ||
friend.newCurrentGame.connect(self._on_new_friend_game) | ||
self._reconnect_game_signals() | ||
|
||
def _on_new_friend_game(self): | ||
self._reconnect_game_signals() | ||
self._check_game_joining_event() | ||
|
||
def _reconnect_game_signals(self): | ||
old_game = self._friend_game | ||
if old_game is not None: | ||
old_game.liveReplayAvailable.disconnect( | ||
self._check_game_replay_event) | ||
|
||
new_game = self._friend.currentGame | ||
self._friend_game = new_game | ||
if new_game is not None: | ||
new_game.liveReplayAvailable.connect( | ||
self._check_game_replay_event) | ||
|
||
def _check_game_joining_event(self): | ||
if self._friend_game is None: | ||
return | ||
if self._friend_game.state == GameState.OPEN: | ||
if self._friend_game.host == self._friend.login: | ||
self.friendEvent.emit(self._friend, FriendEvents.HOSTING_GAME) | ||
else: | ||
self.friendEvent.emit(self._friend, FriendEvents.JOINED_GAME) | ||
|
||
def _check_game_replay_event(self): | ||
if self._friend_game is None: | ||
return | ||
if not self._friend_game.has_live_replay: | ||
return | ||
self.friendEvent.emit(self._friend, FriendEvents.REPLAY_AVAILABLE) | ||
|
||
def report_all_events(self): | ||
self._check_game_joining_event() | ||
self._check_game_replay_event() | ||
|
||
|
||
class FriendsEventTracker(QObject): | ||
""" | ||
Forwards notifications about all online friend players. | ||
FIXME: we duplicate all friend tracker signals here, is there a more | ||
elegant way? Maybe an enum and a single signal? | ||
""" | ||
friendEvent = pyqtSignal(object, object) | ||
|
||
def __init__(self, online_friend_tracker): | ||
QObject.__init__(self) | ||
self._online_friend_tracker = online_friend_tracker | ||
self._friend_event_trackers = {} | ||
|
||
self._online_friend_tracker.friendAdded.connect(self._add_friend) | ||
self._online_friend_tracker.friendRemoved.connect(self._remove_friend) | ||
|
||
for friend in self._online_friend_tracker.friends: | ||
self._add_friend(friend) | ||
|
||
def _add_friend(self, friend): | ||
tracker = FriendEventTracker(friend) | ||
tracker.friendEvent.connect(self.friendEvent.emit) | ||
self._friend_event_trackers[friend.id] = tracker | ||
|
||
# No risk of reporting an event twice - either it didn't happen yet | ||
# so it won't be reported here, or it happened already so it wasn't | ||
# tracked | ||
tracker.report_all_events() | ||
|
||
def _remove_friend(self, friend): | ||
try: | ||
# Signals get disconnected automatically since tracker is | ||
# no longer referenced. | ||
del self._friend_event_trackers[friend.id] | ||
except KeyError: | ||
pass | ||
|
||
|
||
def build_friends_tracker(me, playerset): | ||
online_friend_tracker = OnlineFriendsTracker(me, playerset) | ||
friends_event_tracker = FriendsEventTracker(online_friend_tracker) | ||
return friends_event_tracker |
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 |
---|---|---|
@@ -1,61 +1,86 @@ | ||
from PyQt5.QtCore import QTimer | ||
from model.game import GameState | ||
|
||
from fa import maps | ||
from chat.friendtracker import build_friends_tracker, FriendEvents | ||
import time | ||
|
||
|
||
class GameAnnouncer: | ||
ANNOUNCE_DELAY_SECS = 35 | ||
|
||
def __init__(self, gameset, me, colors, client): | ||
self._gameset = gameset | ||
def __init__(self, playerset, me, colors, client): | ||
self._me = me | ||
self._colors = colors | ||
self._client = client | ||
|
||
self._gameset.newLobby.connect(self._announce_hosting) | ||
self._gameset.newLiveReplay.connect(self._announce_replay) | ||
self._friends_event_tracker = build_friends_tracker(me, playerset) | ||
self._friends_event_tracker.friendEvent.connect(self._friend_event) | ||
|
||
self.announce_games = True | ||
self.announce_replays = True | ||
self._delayed_host_list = [] | ||
self._delayed_event_list = [] | ||
self.delay_friend_events = True | ||
|
||
def _is_friend_host(self, game): | ||
return (game.host_player is not None | ||
and self._me.isFriend(game.host_player.id)) | ||
def _friend_event(self, player, event): | ||
if self.delay_friend_events: | ||
self._delayed_event_list.append((player, event)) | ||
else: | ||
self._friend_announce(player, event) | ||
|
||
def _announce_hosting(self, game): | ||
if not self._is_friend_host(game) or not self.announce_games: | ||
def delayed_friend_events(self, player): | ||
if not self.delay_friend_events: | ||
return | ||
announce_delay = QTimer() | ||
announce_delay.setSingleShot(True) | ||
announce_delay.setInterval(self.ANNOUNCE_DELAY_SECS * 1000) | ||
announce_delay.timeout.connect(self._delayed_announce_hosting) | ||
announce_delay.start() | ||
self._delayed_host_list.append((announce_delay, game)) | ||
|
||
def _delayed_announce_hosting(self): | ||
timer, game = self._delayed_host_list.pop(0) | ||
|
||
if (not self._is_friend_host(game) or | ||
not self.announce_games or | ||
game.state != GameState.OPEN): | ||
if len(self._delayed_event_list) == 0: | ||
self.delay_friend_events = False | ||
return | ||
self._announce(game, "hosting") | ||
i = 0 | ||
for event in self._delayed_event_list: | ||
if player in event: | ||
player, event = self._delayed_event_list.pop(i) | ||
self._friend_announce(player, event) | ||
i += 1 | ||
|
||
def _announce_replay(self, game): | ||
if not self._is_friend_host(game) or not self.announce_replays: | ||
def _friend_announce(self, player, event): | ||
if player.currentGame is None: | ||
return | ||
game = player.currentGame | ||
if event == FriendEvents.HOSTING_GAME: | ||
if not self.announce_games: # Menu Option Chat | ||
return | ||
if game.featured_mod == "ladder1v1": | ||
activity = "started" | ||
else: | ||
activity = "is <font color='GoldenRod'>hosting</font>" | ||
elif event == FriendEvents.JOINED_GAME: | ||
if not self.announce_games: # Menu Option Chat | ||
return | ||
if game.featured_mod == "ladder1v1": | ||
activity = "started" | ||
else: | ||
activity = "joined" | ||
elif event == FriendEvents.REPLAY_AVAILABLE: | ||
if not self.announce_replays: # Menu Option Chat | ||
return | ||
activity = "is playing live" | ||
else: # that shouldn't happen | ||
return | ||
self._announce(game, "playing live") | ||
|
||
def _announce(self, game, activity): | ||
url = game.url(game.host_player.id).toString() | ||
url_color = self._colors.getColor("url") | ||
mapname = maps.getDisplayName(game.mapname) | ||
fmt = 'is {} {}<a style="color:{}" href="{}">{}</a> (on {})' | ||
if game.featured_mod == "faf": | ||
modname = "" | ||
else: | ||
modname = game.featured_mod + " " | ||
msg = fmt.format(activity, modname, url_color, url, game.title, mapname) | ||
self._client.forwardLocalBroadcast(game.host, msg) | ||
if game.featured_mod != "ladder1v1": | ||
player_info = " [{}/{}]".format(game.num_players, game.max_players) | ||
else: | ||
player_info = "" | ||
time_info = "" | ||
if game.has_live_replay: | ||
time_running = time.time() - game.launched_at | ||
if time_running > 6 * 60: # already running games on client start | ||
time_format = '%M:%S' if time_running < 60 * 60 else '%H:%M:%S' | ||
time_info = " runs {}"\ | ||
.format(time.strftime(time_format, time.gmtime(time_running))) | ||
url_color = self._colors.getColor("url") | ||
url = game.url(player.id).toString() | ||
|
||
fmt = '{} {}<a style="color:{}" href="{}">{}</a> ' \ | ||
'(on <font color="GoldenRod">{}</font> {}{})' | ||
msg = fmt.format(activity, modname, url_color, url, game.title, | ||
game.mapdisplayname, player_info, time_info) | ||
self._client.forwardLocalBroadcast(player.login, msg) |