317 lines
10 KiB
Java
317 lines
10 KiB
Java
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import javax.swing.*;
|
|
import javax.swing.event.*;
|
|
import javax.swing.border.*;
|
|
import java.net.*;
|
|
import java.io.*;
|
|
import java.util.*;
|
|
|
|
public class BlobWars extends JFrame
|
|
{
|
|
private BlobWarsWorld m_world;
|
|
private BlobWarsPanel m_panel;
|
|
private JTextField m_nameField;
|
|
private JTextField m_serverField;
|
|
private JLabel m_statusLabel;
|
|
private JButton m_connectButton;
|
|
private JButton m_disconnectButton;
|
|
private EventHandler m_handler;
|
|
private javax.swing.Timer m_timer;
|
|
private Socket m_socket;
|
|
private BufferedWriter m_writer;
|
|
private Vector<String> m_serverUpdates;
|
|
private long m_statusUpdateTime;
|
|
|
|
public BlobWars()
|
|
{
|
|
super("Josh's Blob Wars Game for CS654");
|
|
|
|
m_serverUpdates = new Vector<String>();
|
|
m_world = new BlobWarsWorld();
|
|
|
|
m_handler = new EventHandler();
|
|
m_panel = new BlobWarsPanel(this, m_world);
|
|
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
|
setResizable(false);
|
|
getContentPane().add(new JPanel() {{
|
|
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
|
add(new JPanel() {{
|
|
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
|
add(new JLabel("Player name: "));
|
|
add(m_nameField = new JTextField());
|
|
}});
|
|
add(new JPanel() {{
|
|
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
|
add(new JLabel("Server: "));
|
|
add(m_serverField = new JTextField());
|
|
m_serverField.addKeyListener(m_handler);
|
|
add(m_connectButton = new JButton("Connect"));
|
|
m_connectButton.addActionListener(m_handler);
|
|
add(m_disconnectButton = new JButton("Disconnect"));
|
|
m_disconnectButton.addActionListener(m_handler);
|
|
m_disconnectButton.setEnabled(false);
|
|
}});
|
|
add(new JPanel() {{
|
|
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
|
add(m_statusLabel = new JLabel());
|
|
}});
|
|
// add(new Box.Filler(new Dimension(0, 0),
|
|
// new Dimension(0, 10000),
|
|
// new Dimension(10000, 10000)));
|
|
// add(new JPanel() {{
|
|
// setLayout(new BorderLayout());
|
|
add(m_panel);
|
|
m_panel.addKeyListener(m_handler);
|
|
// }});
|
|
}});
|
|
setSize(getPreferredSize());
|
|
setVisible(true);
|
|
}
|
|
|
|
public String getPlayerName()
|
|
{
|
|
return m_nameField.getText();
|
|
}
|
|
|
|
private void doConnect()
|
|
{
|
|
try {
|
|
m_socket = new Socket(m_serverField.getText(), BlobWarsServer.PORT);
|
|
m_writer = new BufferedWriter(
|
|
new OutputStreamWriter(m_socket.getOutputStream()));
|
|
m_writer.write("NAME:" + m_nameField.getText() + "\n");
|
|
m_writer.flush();
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
JOptionPane.showMessageDialog(null,
|
|
"Error connecting: " + e.getMessage());
|
|
return;
|
|
}
|
|
m_connectButton.setEnabled(false);
|
|
m_nameField.setEnabled(false);
|
|
m_serverField.setEnabled(false);
|
|
m_disconnectButton.setEnabled(true);
|
|
m_panel.requestFocusInWindow();
|
|
(new Thread(new ClientHandler())).start();
|
|
m_timer = new javax.swing.Timer(50, m_handler);
|
|
m_timer.addActionListener(m_handler);
|
|
m_timer.start();
|
|
}
|
|
|
|
private void doDisconnect()
|
|
{
|
|
m_timer.stop();
|
|
m_connectButton.setEnabled(true);
|
|
m_disconnectButton.setEnabled(false);
|
|
m_nameField.setEnabled(true);
|
|
m_serverField.setEnabled(true);
|
|
m_world.clear();
|
|
}
|
|
|
|
private class ClientHandler implements Runnable
|
|
{
|
|
public void run()
|
|
{
|
|
BufferedReader br;
|
|
try {
|
|
br = new BufferedReader(
|
|
new InputStreamReader(m_socket.getInputStream()));
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
return;
|
|
}
|
|
|
|
/* main client loop */
|
|
boolean exitingDueToNameExisting = false;
|
|
for (;;)
|
|
{
|
|
String line = "";
|
|
try {
|
|
line = br.readLine();
|
|
} catch (Exception e) {
|
|
// e.printStackTrace();
|
|
break;
|
|
}
|
|
if (line == null)
|
|
break;
|
|
// System.out.println("GOT LINE: " + line);
|
|
if (line.equals("NAMEEXISTS"))
|
|
exitingDueToNameExisting = true;
|
|
synchronized (m_serverUpdates)
|
|
{
|
|
m_serverUpdates.add(line);
|
|
}
|
|
}
|
|
if (exitingDueToNameExisting)
|
|
{
|
|
SwingUtilities.invokeLater(new Runnable() {
|
|
public void run()
|
|
{
|
|
m_statusLabel.setText("Player '"
|
|
+ m_nameField.getText()
|
|
+ "' already exists!");
|
|
}
|
|
});
|
|
}
|
|
SwingUtilities.invokeLater(new Runnable() {
|
|
public void run()
|
|
{
|
|
doDisconnect();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
private class EventHandler implements ActionListener, KeyListener
|
|
{
|
|
public void actionPerformed(ActionEvent e)
|
|
{
|
|
if (e.getSource() == m_connectButton)
|
|
{
|
|
doConnect();
|
|
}
|
|
else if (e.getSource() == m_disconnectButton)
|
|
{
|
|
if (m_socket != null)
|
|
{
|
|
try {
|
|
m_socket.close();
|
|
} catch (Exception ex) {}
|
|
doDisconnect();
|
|
}
|
|
}
|
|
else if (e.getSource() == m_timer)
|
|
{
|
|
/* m_world.step(); /* step the world like the server would
|
|
* on its end. this provides motion
|
|
* prediction by carrying out the same
|
|
* actions the server would assuming that
|
|
* player directions have not changed
|
|
* since the last time step if we have
|
|
* not heard anything */
|
|
processUpdates();
|
|
if (((new Date()).getTime() - m_statusUpdateTime) > 5000)
|
|
if (!m_statusLabel.getText().equals(""))
|
|
m_statusLabel.setText("");
|
|
m_panel.repaint();
|
|
}
|
|
}
|
|
|
|
public void keyPressed(KeyEvent e)
|
|
{
|
|
if (e.getSource() == m_panel)
|
|
{
|
|
try {
|
|
switch (e.getKeyCode())
|
|
{
|
|
case KeyEvent.VK_UP:
|
|
m_writer.write("UP\n");
|
|
m_writer.flush();
|
|
break;
|
|
case KeyEvent.VK_DOWN:
|
|
m_writer.write("DOWN\n");
|
|
m_writer.flush();
|
|
break;
|
|
case KeyEvent.VK_LEFT:
|
|
m_writer.write("LEFT\n");
|
|
m_writer.flush();
|
|
break;
|
|
case KeyEvent.VK_RIGHT:
|
|
m_writer.write("RIGHT\n");
|
|
m_writer.flush();
|
|
break;
|
|
case KeyEvent.VK_SPACE:
|
|
m_writer.write("SHOOT\n");
|
|
m_writer.flush();
|
|
break;
|
|
}
|
|
} catch (Exception ex) {}
|
|
}
|
|
else if (e.getSource() == m_serverField)
|
|
{
|
|
if (e.getKeyCode() == KeyEvent.VK_ENTER)
|
|
{
|
|
doConnect();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void keyReleased(KeyEvent e)
|
|
{
|
|
if (e.getSource() == m_panel)
|
|
{
|
|
try {
|
|
switch (e.getKeyCode())
|
|
{
|
|
case KeyEvent.VK_LEFT:
|
|
m_writer.write("NOSPIN\n");
|
|
m_writer.flush();
|
|
break;
|
|
case KeyEvent.VK_RIGHT:
|
|
m_writer.write("NOSPIN\n");
|
|
m_writer.flush();
|
|
break;
|
|
}
|
|
} catch (Exception ex) {}
|
|
}
|
|
}
|
|
|
|
public void keyTyped(KeyEvent e)
|
|
{
|
|
}
|
|
}
|
|
|
|
private void processUpdates()
|
|
{
|
|
Vector<String> updates = new Vector<String>();
|
|
synchronized (m_serverUpdates)
|
|
{
|
|
updates.addAll(m_serverUpdates);
|
|
m_serverUpdates.clear();
|
|
}
|
|
|
|
for (String s : updates)
|
|
{
|
|
if (s.startsWith("PLAYER:"))
|
|
{
|
|
String playerInfo = s.substring(7);
|
|
// System.out.println("processUpdates: calling updatePlayer(\"" + playerInfo + "\")");
|
|
m_world.updatePlayer(playerInfo);
|
|
}
|
|
else if (s.startsWith("SHOT:"))
|
|
{
|
|
String shotInfo = s.substring(5);
|
|
// System.out.println("processUpdates: calling updateShot(\"" + shotInfo + "\")");
|
|
m_world.updateShot(shotInfo);
|
|
}
|
|
else if (s.startsWith("RMPLAYER:"))
|
|
{
|
|
String playerName = s.substring(9);
|
|
m_world.removePlayer(playerName);
|
|
}
|
|
else if (s.startsWith("RMSHOT:"))
|
|
{
|
|
Integer shotid = Integer.parseInt(s.substring(7));
|
|
m_world.removeShot(shotid);
|
|
}
|
|
else if (s.startsWith("WINNER:"))
|
|
{
|
|
String winner = s.substring(7);
|
|
m_statusLabel.setText(winner + " won the match!");
|
|
m_statusUpdateTime = (new Date()).getTime();
|
|
}
|
|
else if (s.equals("NAMEEXISTS"))
|
|
{
|
|
doDisconnect();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args)
|
|
{
|
|
new BlobWars();
|
|
}
|
|
}
|