Some more network stuff. We almost have a working lobby!

This commit is contained in:
elcnesh
2015-02-27 13:02:20 +00:00
parent 7ff89bd6b2
commit ca82a3b0d9
25 changed files with 462 additions and 106 deletions

View File

@@ -1,23 +1,30 @@
package forge.gui;
import forge.model.FModel;
import forge.net.FGameClient;
import forge.net.game.MessageEvent;
import forge.properties.ForgePreferences.FPref;
import forge.toolbox.*;
import forge.toolbox.FSkin.SkinnedPanel;
import net.miginfocom.swing.MigLayout;
import org.apache.commons.lang3.StringUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.ScrollPaneConstants;
import net.miginfocom.swing.MigLayout;
import org.apache.commons.lang3.StringUtils;
import forge.model.FModel;
import forge.net.game.IRemote;
import forge.net.game.MessageEvent;
import forge.properties.ForgePreferences.FPref;
import forge.toolbox.FLabel;
import forge.toolbox.FScrollPane;
import forge.toolbox.FSkin;
import forge.toolbox.FSkin.SkinnedPanel;
import forge.toolbox.FTextArea;
import forge.toolbox.FTextField;
import forge.toolbox.SmartScroller;
/**
* TODO: Write javadoc for this type.
*
@@ -39,9 +46,9 @@ public enum FNetOverlay {
private int height = 120;
private int width = 400;
private FGameClient client = null;
public void setGameClient(final FGameClient client) {
this.client = client;
private IRemote remote = null;
public void setGameClient(final IRemote remote) {
this.remote = remote;
}
private final ActionListener onSend = new ActionListener() {
@@ -52,8 +59,8 @@ public enum FNetOverlay {
return;
}
if (client != null) {
client.send(new MessageEvent(FModel.getPreferences().getPref(FPref.PLAYER_NAME), message));
if (remote != null) {
remote.send(new MessageEvent(FModel.getPreferences().getPref(FPref.PLAYER_NAME), message));
}
// lobby.speak(ChatArea.Room, lobby.getGuiPlayer(), message);
}

View File

@@ -26,11 +26,13 @@ import forge.game.GameType;
import forge.gui.framework.FScreen;
import forge.item.PaperCard;
import forge.model.FModel;
import forge.net.game.LobbySlotType;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.screens.deckeditor.CDeckEditorUI;
import forge.screens.deckeditor.controllers.CEditorCommander;
import forge.screens.deckeditor.controllers.CEditorVariant;
import forge.screens.home.VLobby.LobbyType;
import forge.screens.home.sanctioned.AvatarSelector;
import forge.toolbox.FComboBox;
import forge.toolbox.FComboBoxWrapper;
@@ -52,7 +54,10 @@ public class PlayerPanel extends FPanel {
private static final SkinColor unfocusedPlayerOverlay = FSkin.getColor(FSkin.Colors.CLR_OVERLAY).alphaColor(120);
private final int index;
private final boolean allowRemote;
private final LobbyType lobbyType;
private LobbySlotType type = LobbySlotType.LOCAL;
private boolean editableForClient;
private final FLabel nameRandomiser;
private final FLabel avatarLabel = new FLabel.Builder().opaque(true).hoverable(true).iconScaleFactor(0.99f).iconInBackground(true).build();
@@ -63,12 +68,11 @@ public class PlayerPanel extends FPanel {
private FRadioButton radioAi;
private JCheckBoxMenuItem radioAiUseSimulation;
private FRadioButton radioOpen;
/** Whether this panel is occupied by a remote player. */
private boolean isRemote;
private FComboBoxWrapper<Object> teamComboBox = new FComboBoxWrapper<Object>();
private FComboBoxWrapper<Object> aeTeamComboBox = new FComboBoxWrapper<Object>();
private final FLabel closeBtn;
private final FLabel deckBtn = new FLabel.ButtonBuilder().text("Select a deck").build();
private final FLabel deckLabel;
@@ -91,7 +95,7 @@ public class PlayerPanel extends FPanel {
private final FLabel vgdLabel;
private final VLobby lobby;
public PlayerPanel(final VLobby lobby, final int index, final boolean allowRemote) {
public PlayerPanel(final VLobby lobby, final int index, final LobbyType lobbyType) {
super();
this.lobby = lobby;
this.deckLabel = lobby.newLabel("Deck:");
@@ -101,14 +105,14 @@ public class PlayerPanel extends FPanel {
this.vgdLabel = lobby.newLabel("Vanguard:");
this.index = index;
this.allowRemote = allowRemote;
this.lobbyType = lobbyType;
this.playerIsArchenemy = index == 0;
setLayout(new MigLayout("insets 10px, gap 5px"));
// Add a button to players 3+ (or if server) to remove them from the setup
if (index >= 2 || allowRemote) {
FLabel closeBtn = createCloseButton();
closeBtn = createCloseButton();
if (index >= 2 || lobbyType == LobbyType.SERVER) {
this.add(closeBtn, "w 20, h 20, pos (container.w-20) 0");
}
@@ -132,7 +136,7 @@ public class PlayerPanel extends FPanel {
aeTeamComboBox.addActionListener(teamListener);
teamComboBox.addTo(this, variantBtnConstraints + ", pushx, growx, gaptop 5px");
aeTeamComboBox.addTo(this, variantBtnConstraints + ", pushx, growx, gaptop 5px");
if (allowRemote) {
if (lobbyType == LobbyType.SERVER) {
this.add(radioOpen, "gapleft 1px");
}
@@ -168,13 +172,31 @@ public class PlayerPanel extends FPanel {
update();
}
private void update() {
final boolean enableComponents = !(isOpen() || isRemote());
avatarLabel.setEnabled(enableComponents);
txtPlayerName.setEnabled(enableComponents);
nameRandomiser.setEnabled(enableComponents);
deckLabel.setVisible(enableComponents);
deckBtn.setVisible(enableComponents);
void update() {
if (type != LobbySlotType.REMOTE) {
if (radioHuman.isSelected()) {
type = LobbySlotType.LOCAL;
} else if (radioAi.isSelected()) {
type = LobbySlotType.AI;
} else if (radioOpen.isSelected()) {
type = LobbySlotType.OPEN;
}
}
final boolean isEditable = lobbyType == LobbyType.LOCAL || type == LobbySlotType.LOCAL ||
(lobbyType == LobbyType.SERVER && (type == LobbySlotType.LOCAL || type == LobbySlotType.AI)) ||
(lobbyType == LobbyType.CLIENT && editableForClient);
avatarLabel.setEnabled(isEditable);
txtPlayerName.setEnabled(isEditable);
nameRandomiser.setEnabled(isEditable);
deckLabel.setVisible(isEditable);
deckBtn.setVisible(isEditable);
final boolean hasSlotControls = lobbyType == LobbyType.LOCAL || (lobbyType == LobbyType.SERVER && type != LobbySlotType.REMOTE);
closeBtn.setVisible(hasSlotControls);
radioAi.setVisible(hasSlotControls);
radioHuman.setVisible(hasSlotControls);
radioOpen.setVisible(hasSlotControls && lobbyType == LobbyType.SERVER);
}
private final FMouseAdapter radioMouseAdapter = new FMouseAdapter() {
@@ -204,6 +226,7 @@ public class PlayerPanel extends FPanel {
prefs.setPref(FPref.PLAYER_NAME, newName);
prefs.save();
}
lobby.firePlayerChangeListener();
}
}
};
@@ -244,6 +267,8 @@ public class PlayerPanel extends FPanel {
if (index < 2) {
PlayerPanel.this.lobby.updateAvatarPrefs();
}
lobby.firePlayerChangeListener();
}
@Override public final void onRightClick(final MouseEvent e) {
if (!avatarLabel.isEnabled()) {
@@ -334,29 +359,38 @@ public class PlayerPanel extends FPanel {
return index;
}
LobbySlotType getType() {
return type;
}
public boolean isAi() {
return radioAi.isSelected();
return type == LobbySlotType.AI;
}
public boolean isSimulatedAi() {
return radioAi.isSelected() && radioAiUseSimulation.isSelected();
}
public boolean isOpen() {
return radioOpen.isSelected() && !isRemote;
public boolean isLocal() {
return type == LobbySlotType.LOCAL;
}
public boolean isArchenemy() {
return playerIsArchenemy;
}
public boolean isRemote() {
return isRemote;
public void setRemote(final boolean remote) {
if (remote) {
type = LobbySlotType.REMOTE;
} else {
radioOpen.setSelected(true);
type = LobbySlotType.OPEN;
}
update();
}
public void setRemote(final boolean remote) {
isRemote = remote;
update();
public void setEditableForClient(final boolean editable) {
editableForClient = editable;
}
public void setVanguardButtonText(String text) {
@@ -516,9 +550,10 @@ public class PlayerPanel extends FPanel {
* @param index
*/
private void createPlayerTypeOptions() {
radioHuman = new FRadioButton(allowRemote ? "Local" : "Human", index == 0);
radioAi = new FRadioButton("AI", !allowRemote && index != 0);
radioOpen = new FRadioButton("Open", allowRemote && index != 0);
final boolean isServer = lobbyType == LobbyType.SERVER;
radioHuman = new FRadioButton(isServer ? "Local" : "Human", index == 0);
radioAi = new FRadioButton("AI", !isServer && index != 0);
radioOpen = new FRadioButton("Open", isServer && index != 0);
final JPopupMenu menu = new JPopupMenu();
radioAiUseSimulation = new JCheckBoxMenuItem("Use Simulation");
menu.add(radioAiUseSimulation);
@@ -605,7 +640,7 @@ public class PlayerPanel extends FPanel {
.icon(FSkin.getIcon(FSkinProp.ICO_CLOSE)).hoverable(true).build();
closeBtn.setCommand(new Runnable() {
@Override public final void run() {
if (isRemote() && !SOptionPane.showConfirmDialog("Really kick player?", "Kick", false)) {
if (type == LobbySlotType.REMOTE && !SOptionPane.showConfirmDialog(String.format("Really kick %s?", getPlayerName()), "Kick", false)) {
return;
}
PlayerPanel.this.lobby.removePlayer(index);
@@ -639,6 +674,8 @@ public class PlayerPanel extends FPanel {
random = MyRandom.getRandom().nextInt(FSkin.getAvatars().size());
} while (usedAvatars.contains(random));
setAvatar(random);
lobby.firePlayerChangeListener();
}
public void setAvatar(int newAvatarIndex) {

View File

@@ -137,7 +137,7 @@ public enum VHomeUI implements IVTopLevelUI {
allSubmenus.add(VSubmenuSealed.SINGLETON_INSTANCE);
//allSubmenus.add(VSubmenuWinston.SINGLETON_INSTANCE);
//allSubmenus.add(VSubmenuOnlineLobby.SINGLETON_INSTANCE);
allSubmenus.add(VSubmenuOnlineLobby.SINGLETON_INSTANCE);
allSubmenus.add(VSubmenuDuels.SINGLETON_INSTANCE);
allSubmenus.add(VSubmenuChallenges.SINGLETON_INSTANCE);

View File

@@ -35,8 +35,14 @@ import forge.deckchooser.IDecksComboBoxListener;
import forge.game.GameType;
import forge.game.card.CardView;
import forge.gui.CardDetailPanel;
import forge.interfaces.ILobby;
import forge.interfaces.IPlayerChangeListener;
import forge.item.PaperCard;
import forge.model.FModel;
import forge.net.game.LobbySlotType;
import forge.net.game.LobbyState;
import forge.net.game.LobbyState.LobbyPlayerData;
import forge.net.game.server.RemoteClient;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.toolbox.FCheckBox;
@@ -57,13 +63,17 @@ import forge.util.NameGenerator;
*
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
*/
public class VLobby {
public class VLobby implements ILobby {
static final int MAX_PLAYERS = 8;
private static final ForgePreferences prefs = FModel.getPreferences();
public enum LobbyType { LOCAL, SERVER, CLIENT; }
// General variables
private final boolean allowRemote;
private final LobbyType type;
private int localPlayer = 0;
private IPlayerChangeListener playerChangeListener = null;
private final LblHeader lblTitle = new LblHeader("Sanctioned Format: Constructed");
private int activePlayersNum = 2;
private int playerWithFocus = 0; // index of the player that currently has focus
@@ -122,8 +132,9 @@ public class VLobby {
private final Vector<Object> aiListData = new Vector<Object>();
// CTR
public VLobby(final boolean allowRemote) {
this.allowRemote = allowRemote;
public VLobby(final LobbyType type) {
this.type = type;
lblTitle.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2));
////////////////////////////////////////////////////////
@@ -153,7 +164,10 @@ public class VLobby {
teams.add(i + 1);
archenemyTeams.add(i == 0 ? 1 : 2);
final PlayerPanel player = new PlayerPanel(this, i, allowRemote);
final PlayerPanel player = new PlayerPanel(this, i, type);
if (type == LobbyType.CLIENT) {
player.setRemote(true);
}
playerPanels.add(player);
// Populate players panel
@@ -172,14 +186,15 @@ public class VLobby {
playersFrame.setOpaque(false);
playersFrame.add(playersScroll, "w 100%, h 100%-35px");
addPlayerBtn.setFocusable(true);
addPlayerBtn.setCommand(new Runnable() {
@Override
public void run() {
addPlayer();
}
});
playersFrame.add(addPlayerBtn, "height 30px!, growx, pushx");
if (type != LobbyType.CLIENT) {
addPlayerBtn.setFocusable(true);
addPlayerBtn.setCommand(new Runnable() {
@Override public final void run() {
addPlayer();
}
});
playersFrame.add(addPlayerBtn, "height 30px!, growx, pushx");
}
constructedFrame.add(playersFrame, "gapright 10px, w 50%-5px, growy, pushy");
@@ -194,8 +209,10 @@ public class VLobby {
decksFrame.setOpaque(false);
// Start Button
pnlStart.setOpaque(false);
pnlStart.add(btnStart, "align center");
if (type != LobbyType.CLIENT) {
pnlStart.setOpaque(false);
pnlStart.add(btnStart, "align center");
}
}
public void populate() {
@@ -213,20 +230,23 @@ public class VLobby {
}
public void addPlayerInFreeSlot(final String name) {
private int addPlayerInFreeSlot(final String name) {
if (activePlayersNum >= MAX_PLAYERS) {
return;
return -1;
}
for (final PlayerPanel pp : getPlayerPanels()) {
if (pp.isVisible() && pp.isOpen()) {
addPlayer(pp.getIndex());
if (pp.isVisible() && (
pp.getType() == LobbySlotType.OPEN || (pp.isLocal() && type == LobbyType.SERVER))) {
final int index = pp.getIndex();
addPlayer(index);
pp.setPlayerName(name);
pp.setRemote(true);
System.out.println("Put player " + name + " in slot " + index);
return;
return index;
}
}
return -1;
}
private void addPlayer() {
if (activePlayersNum >= MAX_PLAYERS) {
@@ -250,10 +270,12 @@ public class VLobby {
playerPanels.get(slot).setVisible(true);
playerPanels.get(slot).focusOnAvatar();
firePlayerChangeListener();
}
void removePlayer(final int playerIndex) {
if (activePlayersNum < playerIndex) {
if (activePlayersNum <= playerIndex) {
return;
}
activePlayersNum--;
@@ -279,6 +301,69 @@ public class VLobby {
changePlayerFocus(closest);
playerPanels.get(closest).focusOnAvatar();
}
firePlayerChangeListener();
}
@Override
public int login(final RemoteClient client) {
return addPlayerInFreeSlot(client.getUsername());
}
@Override
public void logout(final RemoteClient client) {
removePlayer(client.getIndex());
}
@Override
public LobbyState getState() {
final LobbyState state = new LobbyState();
for (int i = 0; i < activePlayersNum; i++) {
state.addPlayer(getData(i));
}
return state;
}
public void setState(final LobbyState state) {
setLocalPlayer(state.getLocalPlayer());
final List<LobbyPlayerData> players = state.getPlayers();
final int pSize = players.size();
activePlayersNum = pSize;
for (int i = 0; i < pSize; i++) {
final LobbyPlayerData player = players.get(i);
final PlayerPanel panel = playerPanels.get(i);
if (type == LobbyType.CLIENT) {
panel.setRemote(i != localPlayer);
panel.setEditableForClient(i == localPlayer);
} else {
panel.setRemote(player.getType() == LobbySlotType.REMOTE);
panel.setEditableForClient(false);
}
panel.setPlayerName(player.getName());
panel.setAvatar(player.getAvatarIndex());
panel.setVisible(true);
panel.update();
}
}
private void setLocalPlayer(final int index) {
localPlayer = index;
}
public void setPlayerChangeListener(final IPlayerChangeListener listener) {
this.playerChangeListener = listener;
}
void firePlayerChangeListener() {
if (playerChangeListener != null) {
playerChangeListener.update(getData(localPlayer));
}
}
private LobbyPlayerData getData(final int index) {
final PlayerPanel panel = playerPanels.get(index);
return new LobbyPlayerData(panel.getPlayerName(), panel.getAvatarIndex(), panel.getType());
}
/** Builds the actual deck panel layouts for each player.
@@ -373,7 +458,7 @@ public class VLobby {
private void populateDeckPanel(final GameType forGameType) {
decksFrame.removeAll();
if (playerPanelWithFocus.isOpen() || playerPanelWithFocus.isRemote()) {
if (playerPanelWithFocus.getType() == LobbySlotType.OPEN || playerPanelWithFocus.getType() == LobbySlotType.REMOTE) {
return;
}
@@ -439,8 +524,8 @@ public class VLobby {
return deckChoosers.get(playernum);
}
public List<Integer> getTeams() { return Collections.unmodifiableList(teams); }
public List<Integer> getArchenemyTeams() { return Collections.unmodifiableList(archenemyTeams); }
public List<Integer> getTeams() { return teams; }
public List<Integer> getArchenemyTeams() { return archenemyTeams; }
public GameType getCurrentGameMode() { return currentGameMode; }
public void setCurrentGameMode(final GameType mode) { currentGameMode = mode; }

View File

@@ -3,12 +3,17 @@ package forge.screens.home.online;
import forge.UiCommand;
import forge.gui.framework.ICDoc;
import forge.screens.home.CLobby;
import forge.screens.home.VLobby;
public enum COnlineLobby implements ICDoc {
SINGLETON_INSTANCE;
private final VOnlineLobby view = VOnlineLobby.SINGLETON_INSTANCE;
private final CLobby lobby = new CLobby(view.getLobby());
private CLobby lobby;
void setLobby(VLobby lobbyView) {
lobby = new CLobby(lobbyView);
initialize();
}
@Override
public void register() {
@@ -19,7 +24,9 @@ public enum COnlineLobby implements ICDoc {
*/
@Override
public void update() {
lobby.update();
if (lobby != null) {
lobby.update();
}
}
/* (non-Javadoc)
@@ -27,7 +34,9 @@ public enum COnlineLobby implements ICDoc {
*/
@Override
public void initialize() {
lobby.initialize();
if (lobby != null) {
lobby.initialize();
}
}
/* (non-Javadoc)

View File

@@ -13,40 +13,71 @@ import forge.game.GameType;
import forge.gui.FNetOverlay;
import forge.gui.framework.FScreen;
import forge.gui.framework.ICDoc;
import forge.interfaces.IPlayerChangeListener;
import forge.menus.IMenuProvider;
import forge.menus.MenuUtil;
import forge.model.FModel;
import forge.net.FGameClient;
import forge.net.FServerManager;
import forge.net.game.LobbyState;
import forge.net.game.LobbyState.LobbyPlayerData;
import forge.net.game.LoginEvent;
import forge.net.game.client.ILobbyListener;
import forge.net.game.server.RemoteClient;
import forge.properties.ForgePreferences.FPref;
import forge.screens.home.VLobby;
import forge.screens.home.VLobby.LobbyType;
import forge.screens.home.sanctioned.ConstructedGameMenu;
public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
SINGLETON_INSTANCE;
final void host(final int portNumber) {
final VLobby lobby = VOnlineLobby.SINGLETON_INSTANCE.setLobby(LobbyType.SERVER);
FServerManager.getInstance().startServer(portNumber);
FServerManager.getInstance().registerLobbyListener(new ILobbyListener() {
@Override public final void logout(final RemoteClient client) {
FServerManager.getInstance().setLobby(lobby);
FServerManager.getInstance().hostGame(new GameRules(GameType.Constructed));
FNetOverlay.SINGLETON_INSTANCE.showUp("Hosting game");
lobby.setPlayerChangeListener(new IPlayerChangeListener() {
@Override public final void update(final LobbyPlayerData data) {
FServerManager.getInstance().updateLobbyState();
}
@Override public final void login(final RemoteClient client) {
VOnlineLobby.SINGLETON_INSTANCE.getLobby().addPlayerInFreeSlot(client.getUsername());
});
final FGameClient client = new FGameClient(FModel.getPreferences().getPref(FPref.PLAYER_NAME), "0", GuiBase.getInterface().getNewGuiGame());
FNetOverlay.SINGLETON_INSTANCE.setGameClient(client);
client.addLobbyListener(new ILobbyListener() {
@Override public final void update(final LobbyState state) {
lobby.setState(state);
}
@Override public final void message(final String source, final String message) {
FNetOverlay.SINGLETON_INSTANCE.addMessage(source, message);
}
});
FServerManager.getInstance().hostGame(new GameRules(GameType.Constructed));
client.connect("localhost", portNumber);
Singletons.getControl().setCurrentScreen(FScreen.ONLINE_LOBBY);
FNetOverlay.SINGLETON_INSTANCE.showUp("Hosting game");
FNetOverlay.SINGLETON_INSTANCE.showUp(String.format("Hosting on port %d", portNumber));
}
final void join(final String hostname, final int port) {
final FGameClient client = new FGameClient(FModel.getPreferences().getPref(FPref.PLAYER_NAME), "0", GuiBase.getInterface().getNewGuiGame());
FNetOverlay.SINGLETON_INSTANCE.setGameClient(client);
final VLobby lobby = VOnlineLobby.SINGLETON_INSTANCE.setLobby(LobbyType.CLIENT);
client.addLobbyListener(new ILobbyListener() {
@Override public final void update(final LobbyState state) {
lobby.setState(state);
}
@Override public final void message(final String source, final String message) {
FNetOverlay.SINGLETON_INSTANCE.addMessage(source, message);
}
});
lobby.setPlayerChangeListener(new IPlayerChangeListener() {
@Override public final void update(final LobbyPlayerData data) {
client.send(new LoginEvent(data.getName()));
}
});
client.connect(hostname, port);
Singletons.getControl().setCurrentScreen(FScreen.ONLINE_LOBBY);

View File

@@ -13,6 +13,7 @@ import forge.gui.framework.FScreen;
import forge.gui.framework.IVDoc;
import forge.gui.framework.IVTopLevelUI;
import forge.screens.home.VLobby;
import forge.screens.home.VLobby.LobbyType;
import forge.toolbox.FPanel;
import forge.util.gui.SOptionPane;
import forge.view.FView;
@@ -24,15 +25,19 @@ public enum VOnlineLobby implements IVDoc<COnlineLobby>, IVTopLevelUI {
private final DragTab tab = new DragTab("Lobby");
// General variables
private final VLobby lobby;
private VLobby lobby;
private VOnlineLobby() {
this.lobby = new VLobby(true);
}
VLobby getLobby() {
return lobby;
}
VLobby setLobby(final LobbyType type) {
this.lobby = new VLobby(type);
getLayoutControl().setLobby(lobby);
return this.lobby;
}
@Override
public void populate() {

View File

@@ -13,6 +13,7 @@ import forge.screens.home.EMenuGroup;
import forge.screens.home.IVSubmenu;
import forge.screens.home.VLobby;
import forge.screens.home.VHomeUI;
import forge.screens.home.VLobby.LobbyType;
/**
* Assembles Swing components of constructed submenu singleton.
@@ -27,7 +28,7 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
private DragCell parentCell;
private final DragTab tab = new DragTab("Constructed Mode");
private final VLobby lobby = new VLobby(false);
private final VLobby lobby = new VLobby(LobbyType.LOCAL);
private VSubmenuConstructed() {
}