package games.strategy.engine.framework.startup.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.Action;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import games.strategy.engine.chat.IChatPanel;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.framework.IGame;
import games.strategy.engine.framework.headlessGameServer.HeadlessGameServer;
import games.strategy.engine.framework.networkMaintenance.BanPlayerAction;
import games.strategy.engine.framework.networkMaintenance.BootPlayerAction;
import games.strategy.engine.framework.networkMaintenance.MutePlayerAction;
import games.strategy.engine.framework.networkMaintenance.SetPasswordAction;
import games.strategy.engine.framework.startup.launcher.ILauncher;
import games.strategy.engine.framework.startup.launcher.ServerLauncher;
import games.strategy.engine.framework.startup.login.ClientLoginValidator;
import games.strategy.engine.framework.startup.mc.GameSelectorModel;
import games.strategy.engine.framework.startup.mc.IRemoteModelListener;
import games.strategy.engine.framework.startup.mc.ServerModel;
import games.strategy.engine.lobby.client.ui.action.EditGameCommentAction;
import games.strategy.engine.lobby.client.ui.action.RemoveGameFromLobbyAction;
import games.strategy.engine.pbem.PBEMMessagePoster;
import games.strategy.net.IServerMessenger;
import games.strategy.util.ThreadUtil;
/** Setup panel displayed for hosting a non-lobby network game (using host option from main panel). */
public class ServerSetupPanel extends SetupPanel implements IRemoteModelListener {
private static final long serialVersionUID = -2849872641665561807L;
private final ServerModel m_model;
private JTextField m_portField;
private JTextField m_addressField;
private JTextField m_nameField;
private List<PlayerRow> m_playerRows = new ArrayList<>();
private final GameSelectorModel m_gameSelectorModel;
private JPanel m_info;
private JPanel m_networkPanel;
private final InGameLobbyWatcherWrapper m_lobbyWatcher = new InGameLobbyWatcherWrapper();
public ServerSetupPanel(final ServerModel model, final GameSelectorModel gameSelectorModel) {
m_model = model;
m_gameSelectorModel = gameSelectorModel;
m_model.setRemoteModelListener(this);
createLobbyWatcher();
createComponents();
layoutComponents();
setupListeners();
setWidgetActivation();
internalPlayerListChanged();
}
public void createLobbyWatcher() {
if (m_lobbyWatcher != null) {
m_lobbyWatcher.setInGameLobbyWatcher(InGameLobbyWatcher.newInGameLobbyWatcher(m_model.getMessenger(), this,
m_lobbyWatcher.getInGameLobbyWatcher()));
m_lobbyWatcher.setGameSelectorModel(m_gameSelectorModel);
}
}
public synchronized void repostLobbyWatcher(final IGame iGame) {
if (iGame != null) {
return;
}
if (canGameStart()) {
return;
}
System.out.println("Restarting lobby watcher");
shutDownLobbyWatcher();
ThreadUtil.sleep(1000);
HeadlessGameServer.resetLobbyHostOldExtensionProperties();
createLobbyWatcher();
}
public void shutDownLobbyWatcher() {
if (m_lobbyWatcher != null) {
m_lobbyWatcher.shutDown();
}
}
private void createComponents() {
final IServerMessenger messenger = m_model.getMessenger();
final Color backGround = new JTextField().getBackground();
m_portField = new JTextField("" + messenger.getLocalNode().getPort());
m_portField.setEnabled(true);
m_portField.setEditable(false);
m_portField.setBackground(backGround);
m_portField.setColumns(6);
m_addressField = new JTextField(messenger.getLocalNode().getAddress().getHostAddress());
m_addressField.setEnabled(true);
m_addressField.setEditable(false);
m_addressField.setBackground(backGround);
m_addressField.setColumns(20);
m_nameField = new JTextField(messenger.getLocalNode().getName());
m_nameField.setEnabled(true);
m_nameField.setEditable(false);
m_nameField.setBackground(backGround);
m_nameField.setColumns(20);
m_info = new JPanel();
m_networkPanel = new JPanel();
}
private void layoutComponents() {
setLayout(new BorderLayout());
m_info.setLayout(new GridBagLayout());
m_info.add(new JLabel("Name:"), new GridBagConstraints(0, 0, 1, 1, 0, 0.0, GridBagConstraints.EAST,
GridBagConstraints.NONE, new Insets(5, 10, 0, 5), 0, 0));
m_info.add(new JLabel("Address:"), new GridBagConstraints(0, 1, 1, 1, 0, 0.0, GridBagConstraints.EAST,
GridBagConstraints.NONE, new Insets(5, 10, 0, 5), 0, 0));
m_info.add(new JLabel("Port:"), new GridBagConstraints(0, 2, 1, 1, 0, 0.0, GridBagConstraints.EAST,
GridBagConstraints.NONE, new Insets(5, 10, 0, 5), 0, 0));
m_info.add(m_nameField, new GridBagConstraints(1, 0, 1, 1, 0.5, 1.0, GridBagConstraints.WEST,
GridBagConstraints.BOTH, new Insets(5, 0, 0, 5), 0, 0));
m_info.add(m_addressField, new GridBagConstraints(1, 1, 1, 1, 0.5, 1.0, GridBagConstraints.WEST,
GridBagConstraints.BOTH, new Insets(5, 0, 0, 5), 0, 0));
m_info.add(m_portField, new GridBagConstraints(1, 2, 1, 1, 0.5, 1.0, GridBagConstraints.WEST,
GridBagConstraints.BOTH, new Insets(5, 0, 0, 5), 0, 0));
add(m_info, BorderLayout.NORTH);
}
private void layoutPlayers() {
final JPanel players = new JPanel();
final GridBagLayout layout = new GridBagLayout();
players.setLayout(layout);
final Insets spacing = new Insets(3, 16, 0, 0);
final Insets lastSpacing = new Insets(3, 16, 0, 16);
int gridx = 0;
final boolean disableable = !m_model.getPlayersAllowedToBeDisabled().isEmpty()
|| m_model.getPlayersEnabledListing().containsValue(Boolean.FALSE);
final GridBagConstraints enabledPlayerConstraints = new GridBagConstraints();
if (disableable) {
enabledPlayerConstraints.anchor = GridBagConstraints.WEST;
enabledPlayerConstraints.gridx = gridx++;
enabledPlayerConstraints.insets = new Insets(3, 20, 0, -10);
}
final GridBagConstraints nameConstraints = new GridBagConstraints();
nameConstraints.anchor = GridBagConstraints.WEST;
nameConstraints.gridx = gridx++;
nameConstraints.insets = spacing;
final GridBagConstraints playerConstraints = new GridBagConstraints();
playerConstraints.anchor = GridBagConstraints.WEST;
playerConstraints.gridx = gridx++;
playerConstraints.insets = spacing;
final GridBagConstraints localConstraints = new GridBagConstraints();
localConstraints.anchor = GridBagConstraints.WEST;
localConstraints.gridx = gridx++;
localConstraints.insets = spacing;
final GridBagConstraints typeConstraints = new GridBagConstraints();
typeConstraints.anchor = GridBagConstraints.WEST;
typeConstraints.gridx = gridx++;
typeConstraints.insets = spacing;
final GridBagConstraints allianceConstraints = new GridBagConstraints();
allianceConstraints.anchor = GridBagConstraints.WEST;
allianceConstraints.gridx = gridx++;
allianceConstraints.insets = lastSpacing;
if (disableable) {
final JLabel enableLabel = new JLabel("Use");
enableLabel.setForeground(Color.black);
layout.setConstraints(enableLabel, enabledPlayerConstraints);
players.add(enableLabel);
}
final JLabel nameLabel = new JLabel("Name");
nameLabel.setForeground(Color.black);
layout.setConstraints(nameLabel, nameConstraints);
players.add(nameLabel);
final JLabel playedByLabel = new JLabel("Played by");
playedByLabel.setForeground(Color.black);
layout.setConstraints(playedByLabel, playerConstraints);
players.add(playedByLabel);
final JLabel localLabel = new JLabel("Local");
localLabel.setForeground(Color.black);
layout.setConstraints(localLabel, localConstraints);
players.add(localLabel);
final JLabel typeLabel = new JLabel("Type");
typeLabel.setForeground(Color.black);
layout.setConstraints(typeLabel, typeConstraints);
players.add(typeLabel);
final JLabel allianceLabel = new JLabel("Alliance");
allianceLabel.setForeground(Color.black);
layout.setConstraints(allianceLabel, allianceConstraints);
players.add(allianceLabel);
final Iterator<PlayerRow> iter = m_playerRows.iterator();
if (!iter.hasNext()) {
final JLabel noPlayers = new JLabel("Load a game file first");
layout.setConstraints(noPlayers, nameConstraints);
players.add(noPlayers);
}
while (iter.hasNext()) {
final PlayerRow row = iter.next();
if (disableable) {
layout.setConstraints(row.getEnabledPlayer(), enabledPlayerConstraints);
players.add(row.getEnabledPlayer());
}
layout.setConstraints(row.getName(), nameConstraints);
players.add(row.getName());
layout.setConstraints(row.getPlayer(), playerConstraints);
players.add(row.getPlayer());
layout.setConstraints(row.getLocal(), localConstraints);
players.add(row.getLocal());
layout.setConstraints(row.getType(), typeConstraints);
players.add(row.getType());
layout.setConstraints(row.getAlliance(), allianceConstraints);
players.add(row.getAlliance());
}
removeAll();
add(m_info, BorderLayout.NORTH);
final JScrollPane scroll = new JScrollPane(players, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setBorder(null);
scroll.setViewportBorder(null);
add(scroll, BorderLayout.CENTER);
add(m_networkPanel, BorderLayout.SOUTH);
invalidate();
validate();
}
private void setupListeners() {}
@Override
public void setWidgetActivation() {}
@Override
public void shutDown() {
m_model.setRemoteModelListener(IRemoteModelListener.NULL_LISTENER);
m_model.shutDown();
if (m_lobbyWatcher != null) {
m_lobbyWatcher.shutDown();
}
}
@Override
public void cancel() {
m_model.setRemoteModelListener(IRemoteModelListener.NULL_LISTENER);
m_model.cancel();
if (m_lobbyWatcher != null) {
m_lobbyWatcher.shutDown();
}
}
@Override
public void postStartGame() {
final GameData data = m_gameSelectorModel.getGameData();
data.getProperties().set(PBEMMessagePoster.PBEM_GAME_PROP_NAME, false);
}
@Override
public boolean canGameStart() {
if (m_gameSelectorModel.getGameData() == null || m_model == null) {
return false;
}
final Map<String, String> players = m_model.getPlayersToNodeListing();
if (players == null || players.isEmpty()) {
return false;
}
for (final String player : players.keySet()) {
if (players.get(player) == null) {
return false;
}
}
// make sure at least 1 player is enabled
final Map<String, Boolean> someoneEnabled = m_model.getPlayersEnabledListing();
for (final boolean bool : someoneEnabled.values()) {
if (bool) {
return true;
}
}
return false;
}
@Override
public void playerListChanged() {
SwingUtilities.invokeLater(() -> internalPlayerListChanged());
}
@Override
public void playersTakenChanged() {
SwingUtilities.invokeLater(() -> internalPlayersTakenChanged());
}
private void internalPlayersTakenChanged() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("Wrong thread");
}
final Map<String, String> playersToNode = m_model.getPlayersToNodeListing();
final Map<String, Boolean> playersEnabled = m_model.getPlayersEnabledListing();
for (final PlayerRow row : m_playerRows) {
row.update(playersToNode, playersEnabled);
}
super.notifyObservers();
}
private void internalPlayerListChanged() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("Wrong thread");
}
m_playerRows = new ArrayList<>();
final Map<String, String> players = m_model.getPlayersToNodeListing();
final Map<String, Boolean> playersEnabled = m_model.getPlayersEnabledListing();
final Map<String, Collection<String>> m_playerNamesAndAlliancesInTurnOrder =
m_model.getPlayerNamesAndAlliancesInTurnOrderLinkedHashMap();
final Map<String, String> reloadSelections = PlayerID.currentPlayers(m_gameSelectorModel.getGameData());
final Set<String> playerNames = m_playerNamesAndAlliancesInTurnOrder.keySet();
for (final String name : playerNames) {
final PlayerRow newPlayerRow =
new PlayerRow(name, reloadSelections, m_playerNamesAndAlliancesInTurnOrder.get(name),
m_gameSelectorModel.getGameData().getGameLoader().getServerPlayerTypes());
m_playerRows.add(newPlayerRow);
newPlayerRow.update(players, playersEnabled);
}
layoutPlayers();
internalPlayersTakenChanged();
}
class PlayerRow {
private final JLabel m_nameLabel;
private final JLabel m_playerLabel;
private final JCheckBox m_localCheckBox;
private final JCheckBox m_enabledCheckBox;
private final JComboBox<String> m_type;
private JLabel m_alliance;
private final String[] m_types;
PlayerRow(final String playerName, final Map<String, String> reloadSelections,
final Collection<String> playerAlliances, final String[] types) {
m_nameLabel = new JLabel(playerName);
m_playerLabel = new JLabel(m_model.getMessenger().getLocalNode().getName());
m_localCheckBox = new JCheckBox();
m_localCheckBox.addActionListener(m_localPlayerActionListener);
m_localCheckBox.setSelected(true);
m_enabledCheckBox = new JCheckBox();
m_enabledCheckBox.addActionListener(m_disablePlayerActionListener);
// this gets updated later
m_enabledCheckBox.setSelected(true);
m_types = types;
m_type = new JComboBox<>(types);
String previousSelection = reloadSelections.get(playerName);
if (previousSelection.equalsIgnoreCase("Client")) {
previousSelection = types[0];
}
if (!(previousSelection.equals("no_one")) && Arrays.asList(types).contains(previousSelection)) {
m_type.setSelectedItem(previousSelection);
m_model.setLocalPlayerType(m_nameLabel.getText(), (String) m_type.getSelectedItem());
} else if (playerName.startsWith("Neutral") || playerName.startsWith("AI")) {
// the 4th in the list should be Pro AI (Hard AI)
m_type.setSelectedItem(types[Math.max(0, Math.min(types.length - 1, 3))]);
m_model.setLocalPlayerType(m_nameLabel.getText(), (String) m_type.getSelectedItem());
}
if (playerAlliances.contains(playerName)) {
m_alliance = new JLabel();
} else {
m_alliance = new JLabel(playerAlliances.toString());
}
m_type.addActionListener(
e -> m_model.setLocalPlayerType(m_nameLabel.getText(), (String) m_type.getSelectedItem()));
}
public JComboBox<String> getType() {
return m_type;
}
public JLabel getName() {
return m_nameLabel;
}
public JLabel getAlliance() {
return m_alliance;
}
public JLabel getPlayer() {
return m_playerLabel;
}
public JCheckBox getLocal() {
return m_localCheckBox;
}
public JCheckBox getEnabledPlayer() {
return m_enabledCheckBox;
}
public void update(final Map<String, String> playersToNodes, final Map<String, Boolean> playersEnabled) {
String text = playersToNodes.get(m_nameLabel.getText());
if (text == null) {
text = "-";
}
m_playerLabel.setText(text);
m_localCheckBox.setSelected(text.equals(m_model.getMessenger().getLocalNode().getName()));
m_enabledCheckBox.setSelected(playersEnabled.get(m_nameLabel.getText()));
setWidgetActivation();
}
private void setWidgetActivation() {
m_type.setEnabled(m_localCheckBox.isSelected());
m_nameLabel.setEnabled(m_enabledCheckBox.isSelected());
m_playerLabel.setEnabled(m_enabledCheckBox.isSelected());
m_localCheckBox.setEnabled(m_enabledCheckBox.isSelected());
m_alliance.setEnabled(m_enabledCheckBox.isSelected());
m_enabledCheckBox.setEnabled(m_model.getPlayersAllowedToBeDisabled().contains(m_nameLabel.getText()));
}
private final ActionListener m_localPlayerActionListener = new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (m_localCheckBox.isSelected()) {
m_model.takePlayer(m_nameLabel.getText());
} else {
m_model.releasePlayer(m_nameLabel.getText());
}
setWidgetActivation();
}
};
private final ActionListener m_disablePlayerActionListener = new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (m_enabledCheckBox.isSelected()) {
m_model.enablePlayer(m_nameLabel.getText());
// the 1st in the list should be human
m_type.setSelectedItem(m_types[0]);
} else {
m_model.disablePlayer(m_nameLabel.getText());
// the 2nd in the list should be Weak AI
m_type.setSelectedItem(m_types[Math.max(0, Math.min(m_types.length - 1, 1))]);
}
setWidgetActivation();
}
};
}
@Override
public IChatPanel getChatPanel() {
return m_model.getChatPanel();
}
public ServerModel getModel() {
return m_model;
}
@Override
public synchronized ILauncher getLauncher() {
final ServerLauncher launcher = (ServerLauncher) m_model.getLauncher();
if (launcher == null) {
return null;
}
launcher.setInGameLobbyWatcher(m_lobbyWatcher);
return launcher;
}
@Override
public List<Action> getUserActions() {
final List<Action> rVal = new ArrayList<>();
rVal.add(new BootPlayerAction(this, m_model.getMessenger()));
rVal.add(new BanPlayerAction(this, m_model.getMessenger()));
rVal.add(new MutePlayerAction(this, m_model.getMessenger()));
rVal.add(
new SetPasswordAction(this, m_lobbyWatcher, (ClientLoginValidator) m_model.getMessenger().getLoginValidator()));
if (m_lobbyWatcher != null && m_lobbyWatcher.isActive()) {
rVal.add(new EditGameCommentAction(m_lobbyWatcher, ServerSetupPanel.this));
rVal.add(new RemoveGameFromLobbyAction(m_lobbyWatcher));
}
return rVal;
}
@Override
public boolean isMetaSetupPanelInstance() {
return false;
}
}