This commit is contained in:
parent
2bc998a290
commit
9e827864ef
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.launching.PREF_VM_XML=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?>\r\n<vmSettings defaultVM\="57,org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType13,1471573647997" defaultVMConnector\="">\r\n<vmType id\="org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType">\r\n<vm id\="1471573647997" name\="jdk1.6.0_45" path\="C\:\\Program Files\\Java\\jdk1.6.0_45"/>\r\n</vmType>\r\n</vmSettings>\r\n
|
|
@ -0,0 +1,4 @@
|
|||
activeuserprofiles=DESKTOP-SHKMAML;Team
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.rse.systemtype.local.systemType.defaultUserId=Terrans Force
|
||||
useridperkey=DESKTOP-SHKMAML.Local\=Terrans Force;
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -0,0 +1,29 @@
|
|||
INDEX VERSION 1.126+E:\2016Spring\robocodefiles\download\app\workspace_robo4\.metadata\.plugins\org.eclipse.jdt.core
|
||||
3253539546.index
|
||||
240634369.index
|
||||
3357244662.index
|
||||
3837392347.index
|
||||
1270703585.index
|
||||
743580145.index
|
||||
4213330522.index
|
||||
2815204631.index
|
||||
972965919.index
|
||||
649753860.index
|
||||
2097671088.index
|
||||
2394798482.index
|
||||
1886545541.index
|
||||
2420224983.index
|
||||
9299668.index
|
||||
2806151389.index
|
||||
2816454019.index
|
||||
2169288515.index
|
||||
3228954160.index
|
||||
2122446671.index
|
||||
2936589608.index
|
||||
1812437159.index
|
||||
2690281349.index
|
||||
3090911769.index
|
||||
844486800.index
|
||||
195189658.index
|
||||
2555277089.index
|
||||
4256357263.index
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.dotnet.host;
|
||||
|
||||
|
||||
import net.sf.robocode.core.*;
|
||||
import net.sf.robocode.dotnet.repository.items.handlers.DotNetPropertiesHandler;
|
||||
import net.sf.robocode.dotnet.repository.root.handlers.DllHandler;
|
||||
import net.sf.robocode.manager.IVersionManagerBase;
|
||||
import net.sf.robocode.io.Logger;
|
||||
import net.sf.jni4net.Bridge;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
public class Module extends BaseModule {
|
||||
static {
|
||||
// .NET proxies and their interfaces must be loaded in system class loader in order to call native methods
|
||||
Init();
|
||||
}
|
||||
|
||||
private static void Init() {
|
||||
try {
|
||||
|
||||
String libsDir;
|
||||
final String version = ContainerBase.getComponent(IVersionManagerBase.class).getVersionN();
|
||||
|
||||
final java.security.CodeSource source = Module.class.getProtectionDomain().getCodeSource();
|
||||
final File file = new File(source.getLocation().toURI()).getCanonicalFile();
|
||||
|
||||
if (file.getName().equals("classes")) {
|
||||
libsDir = file.getParent();
|
||||
} else if (file.getName().endsWith(".jar")) {
|
||||
libsDir = file.getParent();
|
||||
} else {
|
||||
throw new Error("Can't find " + file);
|
||||
}
|
||||
|
||||
final String nhost = libsDir + "/robocode.dotnet.nhost-" + version + ".dll";
|
||||
final String ncontrol = libsDir + "/robocode.control.dll";
|
||||
|
||||
Bridge.init(new File(libsDir));
|
||||
// Bridge.setVerbose(true);
|
||||
// Bridge.setDebug(true);
|
||||
Bridge.SetSystemClassLoader(Container.engineLoader);
|
||||
Bridge.LoadAndRegisterAssemblyFrom(new File(ncontrol));
|
||||
Bridge.LoadAndRegisterAssemblyFrom(new File(nhost));
|
||||
|
||||
Container.cache.addComponent("DllItemHandler", DllHandler.class);
|
||||
Container.cache.addComponent("CsPropertiesHandler", DotNetPropertiesHandler.class);
|
||||
Container.cache.addComponent("VbPropertiesHandler", DotNetPropertiesHandler.class);
|
||||
Container.cache.addComponent("DotNetPropertiesHandler", DotNetPropertiesHandler.class);
|
||||
|
||||
// .NET proxies
|
||||
Container.cache.addComponent("robocode.host.cs", DotNetHost.class);
|
||||
Container.cache.addComponent("robocode.host.vb", DotNetHost.class);
|
||||
Container.cache.addComponent("robocode.host.dotnet", DotNetHost.class);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
Logger.logError(e);
|
||||
throw new Error("Can't initialize .NET Robocode", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void afterLoaded(List<IModule> allModules) {
|
||||
net.sf.robocode.dotnet.nhost.ModuleN.InitN();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,382 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.installer;
|
||||
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
|
||||
/**
|
||||
* Installer for Robocode.
|
||||
*
|
||||
* @author Mathew A. Nelsen (original)
|
||||
* @author Flemming N. Larsen (contributor)
|
||||
*/
|
||||
public class AutoExtract implements ActionListener {
|
||||
private JDialog licenseDialog;
|
||||
private boolean accepted;
|
||||
private final String[] spinner = { "-", "\\", "|", "/"};
|
||||
private String message = "";
|
||||
private static File installDir;
|
||||
private static final String javaVersion = System.getProperty("java.version");
|
||||
|
||||
private boolean acceptLicense() {
|
||||
String licenseText = "";
|
||||
|
||||
InputStream is;
|
||||
|
||||
try {
|
||||
JarFile extractJar = new JarFile("extract.jar");
|
||||
|
||||
is = extractJar.getInputStream(extractJar.getJarEntry("license/cpl-v10.html"));
|
||||
} catch (IOException e) {
|
||||
return true;
|
||||
}
|
||||
if (is == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BufferedReader r = new BufferedReader(new InputStreamReader(is));
|
||||
|
||||
try {
|
||||
String line = r.readLine();
|
||||
|
||||
while (line != null) {
|
||||
licenseText += line;
|
||||
line = r.readLine();
|
||||
}
|
||||
return acceptReject(licenseText);
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println("Could not read line from license file: " + e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean acceptReject(String text) {
|
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
||||
licenseDialog = new JDialog();
|
||||
licenseDialog.setTitle("License Agreement");
|
||||
licenseDialog.setModal(true);
|
||||
licenseDialog.setLocation((screenSize.width - 500) / 2, (screenSize.height - 400) / 2);
|
||||
licenseDialog.setSize(500, 400);
|
||||
JTextPane t = new JTextPane();
|
||||
|
||||
t.setContentType("text/html");
|
||||
t.setText(text);
|
||||
t.setFont(new Font("Dialog", Font.PLAIN, 12));
|
||||
t.setEditable(false);
|
||||
|
||||
JScrollPane s = new JScrollPane();
|
||||
|
||||
s.setViewportView(t);
|
||||
|
||||
licenseDialog.getContentPane().setLayout(new BorderLayout());
|
||||
licenseDialog.getContentPane().add(s, BorderLayout.CENTER);
|
||||
|
||||
JPanel p = new JPanel();
|
||||
|
||||
p.setLayout(new BorderLayout());
|
||||
JButton b1 = new JButton("Accept");
|
||||
JButton b2 = new JButton("Cancel");
|
||||
|
||||
p.add(b1, BorderLayout.WEST);
|
||||
p.add(b2, BorderLayout.EAST);
|
||||
|
||||
b1.addActionListener(this);
|
||||
b2.addActionListener(this);
|
||||
|
||||
licenseDialog.getContentPane().add(p, BorderLayout.SOUTH);
|
||||
|
||||
licenseDialog.setVisible(true);
|
||||
|
||||
return accepted;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
accepted = e.getActionCommand().equals("Accept");
|
||||
licenseDialog.dispose();
|
||||
licenseDialog = null;
|
||||
}
|
||||
|
||||
private boolean extract(File dest) {
|
||||
JDialog statusDialog = new JDialog();
|
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
||||
int width = 500;
|
||||
int height = 100;
|
||||
|
||||
statusDialog.setTitle("Installing");
|
||||
statusDialog.setLocation((screenSize.width - width) / 2, (screenSize.height - height) / 2);
|
||||
statusDialog.setSize(width, height);
|
||||
JLabel status = new JLabel();
|
||||
|
||||
statusDialog.getContentPane().setLayout(new BorderLayout());
|
||||
statusDialog.getContentPane().add(status, BorderLayout.CENTER);
|
||||
|
||||
statusDialog.setVisible(true);
|
||||
|
||||
FileOutputStream fos;
|
||||
String entryName;
|
||||
|
||||
byte buf[] = new byte[2048];
|
||||
|
||||
final String name = AutoExtract.class.getName().replaceAll("\\.", "/") + ".class";
|
||||
String urlJar = AutoExtract.class.getClassLoader().getResource(name).toString();
|
||||
final String src = urlJar.substring("jar:file:/".length(), urlJar.indexOf("!/"));
|
||||
|
||||
if (src.indexOf('!') > -1) {
|
||||
final String message = src
|
||||
+ "\nContains an exclamation point. Please move the file to a different directory.";
|
||||
|
||||
JOptionPane.showMessageDialog(null, message);
|
||||
System.err.println(message);
|
||||
System.exit(0);
|
||||
}
|
||||
JarInputStream jarIS = null;
|
||||
try {
|
||||
final URL url = new URL("file:/" + src);
|
||||
InputStream is = url.openStream();
|
||||
jarIS = new JarInputStream(is);
|
||||
|
||||
JarEntry entry = jarIS.getNextJarEntry();
|
||||
|
||||
while (entry != null) {
|
||||
int spin = 0;
|
||||
|
||||
entryName = entry.getName();
|
||||
if (entry.isDirectory()) {
|
||||
if (!entryName.startsWith("net")) {
|
||||
File dir = new File(dest, entry.getName());
|
||||
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
System.out.println("Can't create dir " + dir);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!entryName.equals(name)) {
|
||||
status.setText(entryName + " " + spinner[spin++]);
|
||||
|
||||
File out = new File(dest, entry.getName());
|
||||
File parentDirectory = new File(out.getParent());
|
||||
|
||||
if (!parentDirectory.exists() && !parentDirectory.mkdirs()) {
|
||||
System.out.println("Can't create dir " + parentDirectory);
|
||||
}
|
||||
fos = new FileOutputStream(out);
|
||||
|
||||
int index = 0;
|
||||
int num;
|
||||
int count = 0;
|
||||
|
||||
while ((num = jarIS.read(buf, 0, 2048)) != -1) {
|
||||
fos.write(buf, 0, num);
|
||||
index += num;
|
||||
count++;
|
||||
if (count > 80) {
|
||||
status.setText(entryName + " " + spinner[spin++] + " (" + index + " bytes)");
|
||||
if (spin > 3) {
|
||||
spin = 0;
|
||||
}
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
fos.close();
|
||||
|
||||
if (entryName.length() > 3 && entryName.substring(entryName.length() - 3).equals(".sh")) {
|
||||
if (File.separatorChar == '/') {
|
||||
Runtime.getRuntime().exec("chmod 755 " + out.toString());
|
||||
}
|
||||
}
|
||||
|
||||
status.setText(entryName + " " + spinner[spin] + " (" + index + " bytes)");
|
||||
}
|
||||
}
|
||||
entry = jarIS.getNextJarEntry();
|
||||
}
|
||||
statusDialog.dispose();
|
||||
message = "Installation successful";
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
message = "Installation failed" + e;
|
||||
return false;
|
||||
} finally {
|
||||
if (jarIS != null) {
|
||||
try {
|
||||
jarIS.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String argv[]) {
|
||||
// Verify that the Java version is version 6 (1.6.0) or newer
|
||||
if (javaVersion.startsWith("1.") && javaVersion.charAt(2) < '5') {
|
||||
final String message = "Robocode requires Java 6 (1.6.0) or newer.\n"
|
||||
+ "Your system is currently running Java " + javaVersion + ".\n"
|
||||
+ "If you have not installed (or activated) at least\n" + "JRE 6 or JDK 6, please do so.";
|
||||
|
||||
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
|
||||
System.err.println(message);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// Set native look and feel
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (RuntimeException t) {// For some reason Ubuntu 7 can cause a NullPointerException when trying to getting the LAF
|
||||
}
|
||||
|
||||
File suggestedDir;
|
||||
|
||||
AutoExtract extractor = new AutoExtract();
|
||||
|
||||
if (extractor.acceptLicense()) {
|
||||
if (argv.length == 1) {
|
||||
suggestedDir = new File(argv[0]);
|
||||
} else if (File.separatorChar == '\\') {
|
||||
suggestedDir = new File("c:\\robocode\\");
|
||||
} else {
|
||||
suggestedDir = new File(System.getProperty("user.home") + File.separator + "robocode" + File.separator);
|
||||
}
|
||||
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int rc = JOptionPane.showConfirmDialog(null,
|
||||
"Robocode plugin will be installed in:\n" + suggestedDir + "\nIs this ok?", "Installing Robocode",
|
||||
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
if (rc == JOptionPane.YES_OPTION) {
|
||||
installDir = suggestedDir;
|
||||
done = true;
|
||||
} else if (rc == JOptionPane.NO_OPTION) {
|
||||
Object r = JOptionPane.showInputDialog(null, "Please type in the installation directory",
|
||||
"Installation Directory", JOptionPane.PLAIN_MESSAGE, null, null, suggestedDir);
|
||||
|
||||
if (r == null) {
|
||||
JOptionPane.showMessageDialog(null, "Installation cancelled.");
|
||||
System.exit(0);
|
||||
} else {
|
||||
suggestedDir = new File(((String) r).trim());
|
||||
}
|
||||
} else if (rc == JOptionPane.CANCEL_OPTION) {
|
||||
JOptionPane.showMessageDialog(null, "Installation cancelled.");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
if (!installDir.exists()) {
|
||||
int rc = JOptionPane.showConfirmDialog(null,
|
||||
installDir.getPath() + "\ndoes not exist. Would you like to create it?", "Installing Robocode",
|
||||
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
if (rc == JOptionPane.YES_OPTION) {
|
||||
if (!installDir.exists() && !installDir.mkdirs()) {
|
||||
System.out.println("Can't create dir " + installDir);
|
||||
}
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null, "Installation cancelled.");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
boolean rv = extractor.extract(installDir);
|
||||
|
||||
if (!rv) {
|
||||
JOptionPane.showMessageDialog(null, extractor.message);
|
||||
}
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null, "Installation cancelled.");
|
||||
}
|
||||
|
||||
// Delete the class file with the installer and it's parent folders in the robocode home dir
|
||||
if (installDir != null) {
|
||||
String installerPath = AutoExtract.class.getName().replaceAll("\\.", "/") + "$1.class";
|
||||
|
||||
deleteFileAndParentDirsIfEmpty(new File(installDir, installerPath));
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
for (File file : dir.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
// Skip directories ending with ".data"
|
||||
if (file.getName().endsWith(".data")) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// Test for symlink and ignore.
|
||||
// Robocode won't create one, but just in case a user does...
|
||||
if (file.getCanonicalFile().getParentFile().equals(dir.getCanonicalFile())) {
|
||||
deleteDir(file);
|
||||
if (file.exists() && !file.delete()) {
|
||||
System.err.println("Can't delete: " + file);
|
||||
}
|
||||
} else {
|
||||
System.out.println("Warning: " + file + " may be a symlink. It has been ignored");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println(
|
||||
"Warning: Cannot determine canonical file for " + file + ". It has been ignored");
|
||||
}
|
||||
} else {
|
||||
if (file.exists() && !file.delete()) {
|
||||
System.err.println("Can't delete: " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dir.delete();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file and afterwards deletes the parent directories that are empty.
|
||||
*
|
||||
* @param file the file or directory to delete
|
||||
* @return true if success
|
||||
*/
|
||||
private static boolean deleteFileAndParentDirsIfEmpty(final File file) {
|
||||
boolean wasDeleted = false;
|
||||
|
||||
if (file != null && file.exists()) {
|
||||
if (file.isDirectory()) {
|
||||
wasDeleted = deleteDir(file);
|
||||
} else {
|
||||
wasDeleted = file.delete();
|
||||
|
||||
File parent = file;
|
||||
|
||||
while (wasDeleted && (parent = parent.getParentFile()) != null) {
|
||||
// Delete parent directory, but only if it is empty
|
||||
File[] files = parent.listFiles();
|
||||
|
||||
if (files != null && files.length == 0) {
|
||||
wasDeleted = deleteDir(parent);
|
||||
} else {
|
||||
wasDeleted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return wasDeleted;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.test.robotscs;
|
||||
|
||||
|
||||
import net.sf.robocode.test.helpers.Assert;
|
||||
import net.sf.robocode.test.helpers.RobocodeTestBed;
|
||||
|
||||
import robocode.control.events.TurnEndedEvent;
|
||||
|
||||
|
||||
/**
|
||||
* @author Flemming N. Larsen (original)
|
||||
*/
|
||||
public class TestMaxTurnRate extends RobocodeTestBed {
|
||||
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
@Override
|
||||
public String getRobotNames() {
|
||||
return "tested.robotscs.MaxTurnRate,SampleCs.Target";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInitialPositions() {
|
||||
return "(50,50,0), (150,50,0)"; // Make sure the robots do not collide!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTurnEnded(TurnEndedEvent event) {
|
||||
super.onTurnEnded(event);
|
||||
|
||||
buf.append(event.getTurnSnapshot().getRobots()[0].getOutputStreamSnapshot());
|
||||
|
||||
if (event.getTurnSnapshot().getTurn() == 26) {
|
||||
final String out = buf.toString();
|
||||
|
||||
Assert.assertTrue(out.contains("1: 0.0, 0.0") || out.contains("1: 0.0, -0.0"));
|
||||
Assert.assertTrue(out.contains("2: 0.0, -1.0") || out.contains("2: 0.0, -0.999999999"));
|
||||
Assert.assertTrue(out.contains("3: 0.0, -2.0") || out.contains("3: 0.0, -1.999999999"));
|
||||
Assert.assertTrue(out.contains("4: 0.0, -3.0") || out.contains("4: 0.0, -2.999999999"));
|
||||
Assert.assertTrue(out.contains("5: 0.0, -4.0") || out.contains("5: 0.0, -3.999999999"));
|
||||
Assert.assertTrue(out.contains("6: 0.0, -5.0") || out.contains("6: 0.0, -4.999999999"));
|
||||
Assert.assertTrue(out.contains("7: 0.0, -6.0") || out.contains("7: 0.0, -5.999999999"));
|
||||
Assert.assertTrue(out.contains("8: 0.0, -7.0") || out.contains("8: 0.0, -6.999999999"));
|
||||
Assert.assertTrue(out.contains("9: 0.0, -8.0") || out.contains("9: 0.0, -7.999999999"));
|
||||
Assert.assertTrue(out.contains("10: 0.0, -9.0") || out.contains("10: 0.0, -8.999999999"));
|
||||
Assert.assertTrue(out.contains("11: 0.0, -10.0") || out.contains("11: 0.0, -9.999999999"));
|
||||
Assert.assertTrue(out.contains("12: 0.0, -10.0") || out.contains("12: 0.0, -9.999999999"));
|
||||
Assert.assertTrue(out.contains("13: 0.0, -10.0") || out.contains("13: 0.0, -9.999999999"));
|
||||
|
||||
Assert.assertTrue(out.contains("14: 0.0, 0.0") || out.contains("14: 0.0, -0.0"));
|
||||
Assert.assertTrue(out.contains("15: 0.0, 1.0") || out.contains("15: 0.0, 0.999999999"));
|
||||
Assert.assertTrue(out.contains("16: 0.0, 2.0") || out.contains("16: 0.0, 1.999999999"));
|
||||
Assert.assertTrue(out.contains("17: 0.0, 3.0") || out.contains("17: 0.0, 2.999999999"));
|
||||
Assert.assertTrue(out.contains("18: 0.0, 4.0") || out.contains("18: 0.0, 3.999999999"));
|
||||
Assert.assertTrue(out.contains("19: 0.0, 5.0") || out.contains("19: 0.0, 4.999999999"));
|
||||
Assert.assertTrue(out.contains("20: 0.0, 6.0") || out.contains("20: 0.0, 5.999999999"));
|
||||
Assert.assertTrue(out.contains("21: 0.0, 7.0") || out.contains("21: 0.0, 6.999999999"));
|
||||
Assert.assertTrue(out.contains("22: 0.0, 8.0") || out.contains("22: 0.0, 7.999999999"));
|
||||
Assert.assertTrue(out.contains("23: 0.0, 9.0") || out.contains("23: 0.0, 8.999999999"));
|
||||
Assert.assertTrue(out.contains("24: 0.0, 10.0") || out.contains("24: 0.0, 9.999999999"));
|
||||
Assert.assertTrue(out.contains("25: 0.0, 10.0") || out.contains("25: 0.0, 9.999999999"));
|
||||
Assert.assertTrue(out.contains("26: 0.0, 10.0") || out.contains("26: 0.0, 9.999999999"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,389 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.installer;
|
||||
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
|
||||
/**
|
||||
* Installer for Robocode.
|
||||
*
|
||||
* @author Mathew A. Nelsen (original)
|
||||
* @author Flemming N. Larsen (contributor)
|
||||
*/
|
||||
public class AutoExtract implements ActionListener {
|
||||
private JDialog licenseDialog;
|
||||
private boolean accepted;
|
||||
private final String[] spinner = { "-", "\\", "|", "/"};
|
||||
private String message = "";
|
||||
private static final String javaVersion = System.getProperty("java.version");
|
||||
|
||||
/**
|
||||
* AutoExtract constructor.
|
||||
*/
|
||||
public AutoExtract() {
|
||||
super();
|
||||
}
|
||||
|
||||
private boolean acceptLicense() {
|
||||
String licenseText = "";
|
||||
|
||||
InputStream is;
|
||||
|
||||
try {
|
||||
JarFile extractJar = new JarFile("extract.jar");
|
||||
|
||||
is = extractJar.getInputStream(extractJar.getJarEntry("license/cpl-v10.html"));
|
||||
} catch (IOException e) {
|
||||
return true;
|
||||
}
|
||||
if (is == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BufferedReader r = new BufferedReader(new InputStreamReader(is));
|
||||
|
||||
try {
|
||||
String line = r.readLine();
|
||||
|
||||
while (line != null) {
|
||||
licenseText += line;
|
||||
line = r.readLine();
|
||||
}
|
||||
return acceptReject(licenseText);
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println("Could not read line from license file: " + e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean acceptReject(String text) {
|
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
||||
licenseDialog = new JDialog();
|
||||
licenseDialog.setTitle("License Agreement");
|
||||
licenseDialog.setModal(true);
|
||||
licenseDialog.setLocation((screenSize.width - 500) / 2, (screenSize.height - 400) / 2);
|
||||
licenseDialog.setSize(500, 400);
|
||||
JTextPane t = new JTextPane();
|
||||
|
||||
t.setContentType("text/html");
|
||||
t.setText(text);
|
||||
t.setFont(new Font("Dialog", Font.PLAIN, 12));
|
||||
t.setEditable(false);
|
||||
|
||||
JScrollPane s = new JScrollPane();
|
||||
|
||||
s.setViewportView(t);
|
||||
|
||||
licenseDialog.getContentPane().setLayout(new BorderLayout());
|
||||
licenseDialog.getContentPane().add(s, BorderLayout.CENTER);
|
||||
|
||||
JPanel p = new JPanel();
|
||||
|
||||
p.setLayout(new BorderLayout());
|
||||
JButton b1 = new JButton("Accept");
|
||||
JButton b2 = new JButton("Cancel");
|
||||
|
||||
p.add(b1, BorderLayout.WEST);
|
||||
p.add(b2, BorderLayout.EAST);
|
||||
|
||||
b1.addActionListener(this);
|
||||
b2.addActionListener(this);
|
||||
|
||||
licenseDialog.getContentPane().add(p, BorderLayout.SOUTH);
|
||||
|
||||
licenseDialog.setVisible(true);
|
||||
|
||||
return accepted;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
accepted = e.getActionCommand().equals("Accept");
|
||||
licenseDialog.dispose();
|
||||
licenseDialog = null;
|
||||
}
|
||||
|
||||
private boolean extract(File dest) {
|
||||
JDialog statusDialog = new JDialog();
|
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
||||
int width = 500;
|
||||
int height = 100;
|
||||
|
||||
statusDialog.setTitle("Installing");
|
||||
statusDialog.setLocation((screenSize.width - width) / 2, (screenSize.height - height) / 2);
|
||||
statusDialog.setSize(width, height);
|
||||
JLabel status = new JLabel();
|
||||
|
||||
statusDialog.getContentPane().setLayout(new BorderLayout());
|
||||
statusDialog.getContentPane().add(status, BorderLayout.CENTER);
|
||||
|
||||
statusDialog.setVisible(true);
|
||||
|
||||
FileOutputStream fos;
|
||||
String entryName;
|
||||
|
||||
byte buf[] = new byte[2048];
|
||||
|
||||
final String name = AutoExtract.class.getName().replaceAll("\\.", "/") + ".class";
|
||||
String urlJar = AutoExtract.class.getClassLoader().getResource(name).toString();
|
||||
final String src = urlJar.substring("jar:file:/".length(), urlJar.indexOf("!/"));
|
||||
|
||||
if (src.indexOf('!') > -1) {
|
||||
final String message = src
|
||||
+ "\nContains an exclamation point. Please move the file to a different directory.";
|
||||
|
||||
JOptionPane.showMessageDialog(null, message);
|
||||
System.err.println(message);
|
||||
System.exit(0);
|
||||
}
|
||||
JarInputStream jarIS = null;
|
||||
try {
|
||||
final URL url = new URL("file:/" + src);
|
||||
InputStream is = url.openStream();
|
||||
jarIS = new JarInputStream(is);
|
||||
|
||||
JarEntry entry = jarIS.getNextJarEntry();
|
||||
|
||||
while (entry != null) {
|
||||
int spin = 0;
|
||||
|
||||
entryName = entry.getName();
|
||||
if (entry.isDirectory()) {
|
||||
if (!entryName.startsWith("net")) {
|
||||
File dir = new File(dest, entry.getName());
|
||||
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
System.out.println("Can't create dir " + dir);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!entryName.equals(name)) {
|
||||
status.setText(entryName + " " + spinner[spin++]);
|
||||
|
||||
File out = new File(dest, entry.getName());
|
||||
File parentDirectory = new File(out.getParent());
|
||||
|
||||
if (!parentDirectory.exists() && !parentDirectory.mkdirs()) {
|
||||
System.out.println("Can't create dir " + parentDirectory);
|
||||
}
|
||||
fos = new FileOutputStream(out);
|
||||
|
||||
int index = 0;
|
||||
int num;
|
||||
int count = 0;
|
||||
|
||||
while ((num = jarIS.read(buf, 0, 2048)) != -1) {
|
||||
fos.write(buf, 0, num);
|
||||
index += num;
|
||||
count++;
|
||||
if (count > 80) {
|
||||
status.setText(entryName + " " + spinner[spin++] + " (" + index + " bytes)");
|
||||
if (spin > 3) {
|
||||
spin = 0;
|
||||
}
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
fos.close();
|
||||
|
||||
if (entryName.length() > 3 && entryName.substring(entryName.length() - 3).equals(".sh")) {
|
||||
if (File.separatorChar == '/') {
|
||||
Runtime.getRuntime().exec("chmod 755 " + out.toString());
|
||||
}
|
||||
}
|
||||
|
||||
status.setText(entryName + " " + spinner[spin] + " (" + index + " bytes)");
|
||||
}
|
||||
}
|
||||
entry = jarIS.getNextJarEntry();
|
||||
}
|
||||
statusDialog.dispose();
|
||||
message = "Installation successful";
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
message = "Installation failed" + e;
|
||||
return false;
|
||||
} finally {
|
||||
if (jarIS != null) {
|
||||
try {
|
||||
jarIS.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String argv[]) {
|
||||
// Verify that the Java version is version 6 (1.6.0) or newer
|
||||
if (javaVersion.startsWith("1.") && javaVersion.charAt(2) < '5') {
|
||||
final String message = "Robocode requires Java 6 (1.6.0) or newer.\n"
|
||||
+ "Your system is currently running Java " + javaVersion + ".\n"
|
||||
+ "If you have not installed (or activated) at least\n" + "JRE 6 or JDK 6, please do so.";
|
||||
|
||||
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
|
||||
System.err.println(message);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// Set native look and feel
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (RuntimeException t) {// For some reason Ubuntu 7 can cause a NullPointerException when trying to getting the LAF
|
||||
}
|
||||
|
||||
File installDir = null;
|
||||
File suggestedDir;
|
||||
|
||||
AutoExtract extractor = new AutoExtract();
|
||||
|
||||
if (extractor.acceptLicense()) {
|
||||
if (argv.length == 1) {
|
||||
suggestedDir = new File(argv[0]);
|
||||
} else if (File.separatorChar == '\\') {
|
||||
suggestedDir = new File("c:\\robocode\\");
|
||||
} else {
|
||||
suggestedDir = new File(System.getProperty("user.home") + File.separator + "robocode" + File.separator);
|
||||
}
|
||||
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int rc = JOptionPane.showConfirmDialog(null,
|
||||
"Robocode plugin will be installed in:\n" + suggestedDir + "\nIs this ok?", "Installing Robocode",
|
||||
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
if (rc == JOptionPane.YES_OPTION) {
|
||||
installDir = suggestedDir;
|
||||
done = true;
|
||||
} else if (rc == JOptionPane.NO_OPTION) {
|
||||
Object r = JOptionPane.showInputDialog(null, "Please type in the installation directory",
|
||||
"Installation Directory", JOptionPane.PLAIN_MESSAGE, null, null, suggestedDir);
|
||||
|
||||
if (r == null) {
|
||||
JOptionPane.showMessageDialog(null, "Installation cancelled.");
|
||||
System.exit(0);
|
||||
} else {
|
||||
suggestedDir = new File(((String) r).trim());
|
||||
}
|
||||
} else if (rc == JOptionPane.CANCEL_OPTION) {
|
||||
JOptionPane.showMessageDialog(null, "Installation cancelled.");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
if (!installDir.exists()) {
|
||||
int rc = JOptionPane.showConfirmDialog(null,
|
||||
installDir.getPath() + "\ndoes not exist. Would you like to create it?", "Installing Robocode",
|
||||
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
if (rc == JOptionPane.YES_OPTION) {
|
||||
if (!installDir.exists() && !installDir.mkdirs()) {
|
||||
System.out.println("Can't create dir " + installDir);
|
||||
}
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null, "Installation cancelled.");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
boolean rv = extractor.extract(installDir);
|
||||
|
||||
if (!rv) {
|
||||
JOptionPane.showMessageDialog(null, extractor.message);
|
||||
}
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null, "Installation cancelled.");
|
||||
}
|
||||
|
||||
// Delete the class file with the installer and it's parent folders in the robocode home dir
|
||||
if (installDir != null) {
|
||||
String installerPath = AutoExtract.class.getName().replaceAll("\\.", "/") + "$1.class";
|
||||
|
||||
deleteFileAndParentDirsIfEmpty(new File(installDir, installerPath));
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
for (File file : dir.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
// Skip directories ending with ".data"
|
||||
if (file.getName().endsWith(".data")) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// Test for symlink and ignore.
|
||||
// Robocode won't create one, but just in case a user does...
|
||||
if (file.getCanonicalFile().getParentFile().equals(dir.getCanonicalFile())) {
|
||||
deleteDir(file);
|
||||
if (file.exists() && !file.delete()) {
|
||||
System.err.println("Can't delete: " + file);
|
||||
}
|
||||
} else {
|
||||
System.out.println("Warning: " + file + " may be a symlink. It has been ignored");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println(
|
||||
"Warning: Cannot determine canonical file for " + file + ". It has been ignored");
|
||||
}
|
||||
} else {
|
||||
if (file.exists() && !file.delete()) {
|
||||
System.err.println("Can't delete: " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dir.delete();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file and afterwards deletes the parent directories that are empty.
|
||||
*
|
||||
* @param file the file or directory to delete
|
||||
* @return true if success
|
||||
*/
|
||||
private static boolean deleteFileAndParentDirsIfEmpty(final File file) {
|
||||
boolean wasDeleted = false;
|
||||
|
||||
if (file != null && file.exists()) {
|
||||
if (file.isDirectory()) {
|
||||
wasDeleted = deleteDir(file);
|
||||
} else {
|
||||
wasDeleted = file.delete();
|
||||
|
||||
File parent = file;
|
||||
|
||||
while (wasDeleted && (parent = parent.getParentFile()) != null) {
|
||||
// Delete parent directory, but only if it is empty
|
||||
File[] files = parent.listFiles();
|
||||
|
||||
if (files != null && files.length == 0) {
|
||||
wasDeleted = deleteDir(parent);
|
||||
} else {
|
||||
wasDeleted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return wasDeleted;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.security;
|
||||
|
||||
|
||||
import net.sf.robocode.core.ContainerBase;
|
||||
import net.sf.robocode.io.Logger;
|
||||
import net.sf.robocode.peer.IRobotStatics;
|
||||
import robocode.BattleRules;
|
||||
import robocode.Bullet;
|
||||
import robocode.Event;
|
||||
import robocode.RobotStatus;
|
||||
import robocode.control.RobotSpecification;
|
||||
import robocode.control.events.IBattleListener;
|
||||
import robocode.robotinterfaces.IBasicRobot;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* Helpers for accessing hidden methods on events.
|
||||
*
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
public class HiddenAccess {
|
||||
private static IHiddenEventHelper eventHelper;
|
||||
private static IHiddenBulletHelper bulletHelper;
|
||||
private static IHiddenSpecificationHelper specificationHelper;
|
||||
private static IHiddenStatusHelper statusHelper;
|
||||
private static IHiddenRulesHelper rulesHelper;
|
||||
private static Method initContainer;
|
||||
private static Method initContainerRe;
|
||||
private static Method cleanup;
|
||||
private static Method robocodeMain;
|
||||
private static boolean initialized;
|
||||
private static boolean foundCore = false;
|
||||
|
||||
public static void init() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
Method method;
|
||||
|
||||
try {
|
||||
method = Event.class.getDeclaredMethod("createHiddenHelper");
|
||||
method.setAccessible(true);
|
||||
eventHelper = (IHiddenEventHelper) method.invoke(null);
|
||||
method.setAccessible(false);
|
||||
|
||||
method = Bullet.class.getDeclaredMethod("createHiddenHelper");
|
||||
method.setAccessible(true);
|
||||
bulletHelper = (IHiddenBulletHelper) method.invoke(null);
|
||||
method.setAccessible(false);
|
||||
|
||||
method = RobotSpecification.class.getDeclaredMethod("createHiddenHelper");
|
||||
method.setAccessible(true);
|
||||
specificationHelper = (IHiddenSpecificationHelper) method.invoke(null);
|
||||
method.setAccessible(false);
|
||||
|
||||
method = RobotStatus.class.getDeclaredMethod("createHiddenSerializer");
|
||||
method.setAccessible(true);
|
||||
statusHelper = (IHiddenStatusHelper) method.invoke(null);
|
||||
method.setAccessible(false);
|
||||
|
||||
method = BattleRules.class.getDeclaredMethod("createHiddenHelper");
|
||||
method.setAccessible(true);
|
||||
rulesHelper = (IHiddenRulesHelper) method.invoke(null);
|
||||
method.setAccessible(false);
|
||||
|
||||
ClassLoader loader = getClassLoader();
|
||||
Class<?> main = loader.loadClass("net.sf.robocode.core.RobocodeMainBase");
|
||||
|
||||
initContainer = main.getDeclaredMethod("initContainer");
|
||||
initContainer.setAccessible(true);
|
||||
|
||||
initContainerRe = main.getDeclaredMethod("initContainerForRobocodeEngine", File.class, IBattleListener.class);
|
||||
initContainerRe.setAccessible(true);
|
||||
|
||||
cleanup = main.getDeclaredMethod("cleanupForRobocodeEngine");
|
||||
cleanup.setAccessible(true);
|
||||
|
||||
robocodeMain = main.getDeclaredMethod("robocodeMain", Object.class);
|
||||
robocodeMain.setAccessible(true);
|
||||
|
||||
initialized = true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
Logger.logError(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Logger.logError(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Logger.logError(e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Logger.logError(e);
|
||||
if (!foundCore) {
|
||||
Logger.logError("Can't find robocode.core-1.x.jar module near to robocode.jar");
|
||||
Logger.logError("Class path: " + System.getProperty("robocode.class.path", null));
|
||||
}
|
||||
System.exit(-1);
|
||||
} catch (MalformedURLException e) {
|
||||
Logger.logError(e);
|
||||
} catch (MyException e) {
|
||||
Logger.logError(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static ClassLoader getClassLoader() throws MalformedURLException {
|
||||
// if other modules are .jar next to robocode.jar on same path, we will create classloader which will load them
|
||||
// otherwise we rely on that they are already on classpath
|
||||
StringBuilder classPath = new StringBuilder(System.getProperty("java.class.path", null));
|
||||
ClassLoader loader = ClassLoader.getSystemClassLoader();
|
||||
String path = HiddenAccess.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
|
||||
try {
|
||||
path = URLDecoder.decode(path, "UCS2");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
path = new File(".", "libs/robocode.jar").toString();
|
||||
}
|
||||
final int i = path.lastIndexOf("robocode.jar");
|
||||
|
||||
if (i > 0) {
|
||||
loader = createClassLoader(classPath, loader, path.substring(0, i));
|
||||
}
|
||||
System.setProperty("robocode.class.path", classPath.toString());
|
||||
return loader;
|
||||
}
|
||||
|
||||
private static ClassLoader createClassLoader(StringBuilder classPath, ClassLoader loader, String dir) throws MalformedURLException {
|
||||
|
||||
File dirf = new File(dir);
|
||||
ArrayList<URL> urls = new ArrayList<URL>();
|
||||
|
||||
final File[] files = dirf.listFiles(new FilenameFilter() {
|
||||
public boolean accept(File dir, String name) {
|
||||
final String test = name.toLowerCase();
|
||||
|
||||
return test.endsWith(".jar") && !test.endsWith("robocode.jar");
|
||||
}
|
||||
});
|
||||
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
final String name = file.toString().toLowerCase();
|
||||
|
||||
if (name.contains("robocode.core")) {
|
||||
foundCore = true;
|
||||
urls.add(file.toURI().toURL());
|
||||
}
|
||||
if (name.contains("picocontainer")) {
|
||||
urls.add(file.toURI().toURL());
|
||||
}
|
||||
if (name.contains("codesize")) {
|
||||
urls.add(file.toURI().toURL());
|
||||
}
|
||||
classPath.append(File.pathSeparator);
|
||||
classPath.append(file.toString());
|
||||
}
|
||||
}
|
||||
return new URLClassLoader(urls.toArray(new URL[urls.size()]), loader);
|
||||
}
|
||||
|
||||
public static boolean isCriticalEvent(Event e) {
|
||||
return eventHelper.isCriticalEvent(e);
|
||||
}
|
||||
|
||||
public static void setEventTime(Event e, long newTime) {
|
||||
eventHelper.setTime(e, newTime);
|
||||
}
|
||||
|
||||
public static void setEventPriority(Event e, int newPriority) {
|
||||
eventHelper.setPriority(e, newPriority);
|
||||
}
|
||||
|
||||
public static void dispatch(Event event, IBasicRobot robot, IRobotStatics statics, Graphics2D graphics) {
|
||||
eventHelper.dispatch(event, robot, statics, graphics);
|
||||
}
|
||||
|
||||
public static void setDefaultPriority(Event e) {
|
||||
eventHelper.setDefaultPriority(e);
|
||||
}
|
||||
|
||||
public static byte getSerializationType(Event e) {
|
||||
return eventHelper.getSerializationType(e);
|
||||
}
|
||||
|
||||
public static void update(Bullet bullet, double x, double y, String victimName, boolean isActive) {
|
||||
bulletHelper.update(bullet, x, y, victimName, isActive);
|
||||
}
|
||||
|
||||
public static RobotSpecification createSpecification(Object fileSpecification, String name, String author, String webpage, String version, String robocodeVersion, String jarFile, String fullClassName, String description) {
|
||||
return specificationHelper.createSpecification(fileSpecification, name, author, webpage, version,
|
||||
robocodeVersion, jarFile, fullClassName, description);
|
||||
}
|
||||
|
||||
public static Object getFileSpecification(RobotSpecification specification) {
|
||||
return specificationHelper.getFileSpecification(specification);
|
||||
}
|
||||
|
||||
public static String getRobotTeamName(RobotSpecification specification) {
|
||||
return specificationHelper.getTeamName(specification);
|
||||
}
|
||||
|
||||
public static void setTeamId(RobotSpecification specification, String teamName) {
|
||||
specificationHelper.setTeamName(specification, teamName);
|
||||
}
|
||||
|
||||
public static RobotStatus createStatus(double energy, double x, double y, double bodyHeading, double gunHeading, double radarHeading, double velocity,
|
||||
double bodyTurnRemaining, double radarTurnRemaining, double gunTurnRemaining, double distanceRemaining, double gunHeat, int others,
|
||||
int numSentries, int roundNum, int numRounds, long time) {
|
||||
return statusHelper.createStatus(energy, x, y, bodyHeading, gunHeading, radarHeading, velocity,
|
||||
bodyTurnRemaining, radarTurnRemaining, gunTurnRemaining, distanceRemaining, gunHeat, others, numSentries,
|
||||
roundNum, numRounds, time);
|
||||
}
|
||||
|
||||
public static BattleRules createRules(int battlefieldWidth, int battlefieldHeight, int numRounds, double gunCoolingRate, long inactivityTime, boolean hideEnemyNames, int sentryBorderSize) {
|
||||
return rulesHelper.createRules(battlefieldWidth, battlefieldHeight, numRounds, gunCoolingRate, inactivityTime,
|
||||
hideEnemyNames, sentryBorderSize);
|
||||
}
|
||||
|
||||
public static boolean isSafeThread() {
|
||||
final IThreadManagerBase threadManager = ContainerBase.getComponent(IThreadManagerBase.class);
|
||||
|
||||
return threadManager != null && threadManager.isSafeThread();
|
||||
}
|
||||
|
||||
public static void initContainerForRobotEngine(File robocodeHome, IBattleListener listener) {
|
||||
init();
|
||||
try {
|
||||
initContainerRe.invoke(null, robocodeHome, listener);
|
||||
} catch (IllegalAccessException e) {
|
||||
Logger.logError(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Logger.logError(e.getCause());
|
||||
Logger.logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void initContainer() {
|
||||
init();
|
||||
try {
|
||||
initContainer.invoke(null);
|
||||
} catch (IllegalAccessException e) {
|
||||
Logger.logError(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Logger.logError(e.getCause());
|
||||
Logger.logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void cleanup() {
|
||||
init();
|
||||
try {
|
||||
cleanup.invoke(null);
|
||||
} catch (IllegalAccessException e) {
|
||||
Logger.logError(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Logger.logError(e.getCause());
|
||||
Logger.logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void robocodeMain(final String[] args) {
|
||||
init();
|
||||
try {
|
||||
robocodeMain.invoke(null, (Object) args);
|
||||
} catch (IllegalAccessException e) {
|
||||
Logger.logError(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Logger.logError(e.getCause());
|
||||
Logger.logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.battle.events;
|
||||
|
||||
|
||||
import static net.sf.robocode.io.Logger.logError;
|
||||
|
||||
import net.sf.robocode.io.Logger;
|
||||
import robocode.control.events.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* @author Flemming N. Larsen (original)
|
||||
* @author Pavel Savara (original)
|
||||
*
|
||||
* @since 1.6.1
|
||||
*/
|
||||
public class BattleEventDispatcher implements IBattleListener {
|
||||
// This list is guaranteed to be thread-safe, which is necessary as it will be accessed
|
||||
// by both the battle thread and battle manager thread. If this list is not thread-safe
|
||||
// then ConcurentModificationExceptions will occur from time to time.
|
||||
private final List<IBattleListener> listeners = new CopyOnWriteArrayList<IBattleListener>();
|
||||
|
||||
public BattleEventDispatcher() {}
|
||||
|
||||
public void addListener(IBattleListener listener) {
|
||||
assert (listener != null);
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(IBattleListener listener) {
|
||||
assert (listener != null);
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void onBattleStarted(BattleStartedEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onBattleStarted(event);
|
||||
} catch (RuntimeException ex) {
|
||||
logError("onBattleStarted " + listener.getClass(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBattleCompleted(BattleCompletedEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onBattleCompleted(event);
|
||||
} catch (RuntimeException ex) {
|
||||
logError("onBattleCompleted " + listener.getClass(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBattleFinished(BattleFinishedEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onBattleFinished(event);
|
||||
} catch (RuntimeException ex) {
|
||||
logError("onBattleFinished " + listener.getClass(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBattlePaused(BattlePausedEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onBattlePaused(event);
|
||||
} catch (RuntimeException ex) {
|
||||
logError("onBattlePaused " + listener.getClass(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBattleResumed(BattleResumedEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onBattleResumed(event);
|
||||
} catch (RuntimeException ex) {
|
||||
logError("onBattleResumed " + listener.getClass(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onRoundStarted(RoundStartedEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onRoundStarted(event);
|
||||
} catch (RuntimeException ex) {
|
||||
logError("onRoundStarted " + listener.getClass(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onRoundEnded(RoundEndedEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onRoundEnded(event);
|
||||
} catch (RuntimeException ex) {
|
||||
logError("onRoundEnded " + listener.getClass(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onTurnStarted(TurnStartedEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onTurnStarted(event);
|
||||
} catch (RuntimeException ex) {
|
||||
logError("onTurnStarted " + listener.getClass(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onTurnEnded(TurnEndedEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onTurnEnded(event);
|
||||
} catch (RuntimeException ex) {
|
||||
logError("onTurnEnded " + listener.getClass(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBattleMessage(BattleMessageEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onBattleMessage(event);
|
||||
} catch (RuntimeException ex) {
|
||||
logError("onBattleMessage " + listener.getClass(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBattleError(BattleErrorEvent event) {
|
||||
for (IBattleListener listener : listeners) {
|
||||
try {
|
||||
listener.onBattleError(event);
|
||||
} catch (RuntimeException ex) {
|
||||
Logger.realErr.println(listener.getClass() + " " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,414 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.core;
|
||||
|
||||
|
||||
import net.sf.robocode.battle.BattleResultsTableModel;
|
||||
import net.sf.robocode.battle.IBattleManager;
|
||||
import net.sf.robocode.host.ICpuManager;
|
||||
import net.sf.robocode.host.IHostManager;
|
||||
import net.sf.robocode.io.FileUtil;
|
||||
import net.sf.robocode.io.Logger;
|
||||
import net.sf.robocode.io.RobocodeProperties;
|
||||
import net.sf.robocode.recording.BattleRecordFormat;
|
||||
import net.sf.robocode.recording.IRecordManager;
|
||||
import net.sf.robocode.repository.IRepositoryManager;
|
||||
import net.sf.robocode.serialization.SerializableOptions;
|
||||
import net.sf.robocode.settings.ISettingsManager;
|
||||
import net.sf.robocode.sound.ISoundManager;
|
||||
import net.sf.robocode.ui.IWindowManager;
|
||||
import net.sf.robocode.util.StringUtil;
|
||||
import net.sf.robocode.version.IVersionManager;
|
||||
import robocode.control.events.*;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
|
||||
/**
|
||||
* Robocode - A programming game involving battling AI tanks.<br>
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
*
|
||||
* @see <a target="_top" href="http://robocode.sourceforge.net">robocode.sourceforge.net</a>
|
||||
*
|
||||
* @author Mathew A. Nelson (original)
|
||||
* @author Flemming N. Larsen (contributor)
|
||||
* @author Pavel Savara (contributor)
|
||||
*/
|
||||
public final class RobocodeMain extends RobocodeMainBase {
|
||||
|
||||
private final Setup setup;
|
||||
private final BattleObserver battleObserver = new BattleObserver();
|
||||
final private ISettingsManager properties;
|
||||
final private IHostManager hostManager;
|
||||
final private IWindowManager windowManager;
|
||||
final private ISoundManager soundManager;
|
||||
final private IBattleManager battleManager;
|
||||
final private IRecordManager recordManager;
|
||||
final private IVersionManager versionManager;
|
||||
|
||||
private static class Setup {
|
||||
boolean minimize;
|
||||
boolean exitOnComplete;
|
||||
String battleFilename;
|
||||
String recordFilename;
|
||||
String recordXmlFilename;
|
||||
String replayFilename;
|
||||
String resultsFilename;
|
||||
int tps;
|
||||
}
|
||||
|
||||
public RobocodeMain(ISettingsManager properties,
|
||||
IHostManager hostManager,
|
||||
IWindowManager windowManager,
|
||||
ISoundManager soundManager,
|
||||
IBattleManager battleManager,
|
||||
IRecordManager recordManager,
|
||||
IVersionManager versionManager
|
||||
) {
|
||||
setup = new Setup();
|
||||
this.properties = properties;
|
||||
this.hostManager = hostManager;
|
||||
this.windowManager = windowManager;
|
||||
this.soundManager = soundManager;
|
||||
this.battleManager = battleManager;
|
||||
this.recordManager = recordManager;
|
||||
this.versionManager = versionManager;
|
||||
}
|
||||
|
||||
public RobocodeMain(ISettingsManager properties,
|
||||
IHostManager hostManager,
|
||||
IWindowManager windowManager,
|
||||
IBattleManager battleManager,
|
||||
IRecordManager recordManager,
|
||||
IVersionManager versionManager
|
||||
) {
|
||||
this(properties, hostManager, windowManager, null, battleManager, recordManager, versionManager);
|
||||
}
|
||||
|
||||
public RobocodeMain(ISettingsManager properties,
|
||||
IHostManager hostManager,
|
||||
IBattleManager battleManager,
|
||||
IRecordManager recordManager,
|
||||
IVersionManager versionManager
|
||||
) {
|
||||
this(properties, hostManager, null, battleManager, recordManager, versionManager);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
hostManager.initSecurity();
|
||||
|
||||
// Set the Look and Feel (LAF)
|
||||
if (windowManager != null && windowManager.isGUIEnabled()) {
|
||||
windowManager.init();
|
||||
}
|
||||
properties.setOptionsBattleDesiredTPS(setup.tps);
|
||||
|
||||
battleManager.addListener(battleObserver);
|
||||
|
||||
if (windowManager != null && windowManager.isGUIEnabled()) {
|
||||
if (!setup.minimize && setup.battleFilename == null && soundManager != null) {
|
||||
soundManager.playThemeMusic();
|
||||
windowManager.showSplashScreen();
|
||||
}
|
||||
windowManager.showRobocodeFrame(true, setup.minimize);
|
||||
|
||||
// Play the intro battle if a battle file is not specified and this is the first time Robocode is being run
|
||||
|
||||
if (setup.battleFilename == null && versionManager.isLastRunVersionChanged()) {
|
||||
properties.saveProperties();
|
||||
windowManager.runIntroBattle();
|
||||
}
|
||||
}
|
||||
|
||||
final boolean enableCLIRecording = (setup.recordFilename != null || setup.recordXmlFilename != null);
|
||||
|
||||
// Note: At this point the GUI should be opened (if enabled) before starting the battle from a battle file
|
||||
if (setup.battleFilename != null) {
|
||||
if (setup.replayFilename != null) {
|
||||
System.err.println("You cannot run both a battle and replay a battle record in the same time.");
|
||||
System.exit(8);
|
||||
}
|
||||
|
||||
setup.exitOnComplete = true;
|
||||
|
||||
battleManager.setBattleFilename(setup.battleFilename);
|
||||
if (new File(battleManager.getBattleFilename()).exists()) {
|
||||
battleManager.startNewBattle(battleManager.loadBattleProperties(), false, enableCLIRecording);
|
||||
} else {
|
||||
System.err.println("The specified battle file '" + setup.battleFilename + "' was not found");
|
||||
System.exit(8);
|
||||
}
|
||||
} else if (setup.replayFilename != null) {
|
||||
setup.exitOnComplete = true;
|
||||
if (setup.replayFilename.toLowerCase().endsWith("xml.zip")) {
|
||||
recordManager.loadRecord(setup.replayFilename, BattleRecordFormat.XML_ZIP);
|
||||
} else {
|
||||
recordManager.loadRecord(setup.replayFilename, BattleRecordFormat.BINARY_ZIP);
|
||||
}
|
||||
|
||||
if (new File(setup.replayFilename).exists()) {
|
||||
battleManager.replay();
|
||||
} else {
|
||||
System.err.println("The specified battle record file '" + setup.replayFilename + "' was not found");
|
||||
System.exit(8);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Logger.logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadSetup(String[] args) {
|
||||
|
||||
final String nosecMessage = "Robocode is running without a security manager.\n"
|
||||
+ "Robots have full access to your system.\n" + "You should only run robots which you trust!";
|
||||
final String exMessage = "Robocode is running in experimental mode.\n"
|
||||
+ "Robots have access to their IRobotPeer interfaces.\n" + "You should only run robots which you trust!";
|
||||
|
||||
if (RobocodeProperties.isSecurityOff()) {
|
||||
Logger.logWarning(nosecMessage);
|
||||
}
|
||||
if (System.getProperty("EXPERIMENTAL", "false").equals("true")) {
|
||||
Logger.logWarning(exMessage);
|
||||
}
|
||||
|
||||
setup.tps = properties.getOptionsBattleDesiredTPS();
|
||||
|
||||
// Disable canonical file path cache under Windows as it causes trouble when returning
|
||||
// paths with differently-capitalized file names.
|
||||
if (System.getProperty("os.name").toLowerCase().startsWith("windows ")) {
|
||||
System.setProperty("sun.io.useCanonCaches", "false");
|
||||
}
|
||||
|
||||
// Initialize the system property so the AWT does not use headless mode meaning that the
|
||||
// GUI (Awt and Swing) is enabled per default when running starting Robocode.
|
||||
// It might be set to true later, if the -nodisplay option is set (in the setEnableGUI method).
|
||||
// Read more about headless mode here:
|
||||
// http://java.sun.com/developer/technicalArticles/J2SE/Desktop/headless/
|
||||
System.setProperty("java.awt.headless", "false");
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String currentArg = args[i];
|
||||
if (currentArg.equalsIgnoreCase("-cwd") && (i < args.length + 1)) {
|
||||
changeDirectory(args[i + 1]);
|
||||
i++;
|
||||
} else if (currentArg.equalsIgnoreCase("-battle") && (i < args.length + 1)) {
|
||||
setup.battleFilename = args[i + 1];
|
||||
i++;
|
||||
} else if (currentArg.equalsIgnoreCase("-record") && (i < args.length + 1)) {
|
||||
setup.recordFilename = args[i + 1];
|
||||
i++;
|
||||
} else if (currentArg.equalsIgnoreCase("-recordXML") && (i < args.length + 1)) {
|
||||
setup.recordXmlFilename = args[i + 1];
|
||||
i++;
|
||||
} else if (currentArg.equalsIgnoreCase("-replay") && (i < args.length + 1)) {
|
||||
setup.replayFilename = args[i + 1];
|
||||
i++;
|
||||
} else if (currentArg.equalsIgnoreCase("-results") && (i < args.length + 1)) {
|
||||
setup.resultsFilename = args[i + 1];
|
||||
i++;
|
||||
} else if (currentArg.equalsIgnoreCase("-tps") && (i < args.length + 1)) {
|
||||
setup.tps = Integer.parseInt(args[i + 1]);
|
||||
if (setup.tps < 1) {
|
||||
Logger.logError("tps must be > 0");
|
||||
System.exit(8);
|
||||
}
|
||||
i++;
|
||||
} else if (currentArg.equalsIgnoreCase("-minimize")) {
|
||||
setup.minimize = true;
|
||||
} else if (currentArg.equalsIgnoreCase("-nodisplay")) {
|
||||
if (windowManager != null) {
|
||||
windowManager.setEnableGUI(false);
|
||||
}
|
||||
if (soundManager != null) {
|
||||
soundManager.setEnableSound(false);
|
||||
}
|
||||
setup.tps = 10000; // set TPS to maximum
|
||||
} else if (currentArg.equalsIgnoreCase("-nosound")) {
|
||||
if (soundManager != null) {
|
||||
soundManager.setEnableSound(false);
|
||||
}
|
||||
} else if (currentArg.equals("-?") || currentArg.equalsIgnoreCase("-help")) {
|
||||
printUsage();
|
||||
System.exit(0);
|
||||
} else {
|
||||
Logger.logError("Not understood: " + currentArg);
|
||||
printUsage();
|
||||
System.exit(8);
|
||||
}
|
||||
}
|
||||
File robotsDir = FileUtil.getRobotsDir();
|
||||
|
||||
if (robotsDir == null) {
|
||||
System.err.println("No valid robot directory is specified");
|
||||
System.exit(8);
|
||||
} else if (!(robotsDir.exists() && robotsDir.isDirectory())) {
|
||||
System.err.println('\'' + robotsDir.getAbsolutePath() + "' is not a valid robot directory");
|
||||
System.exit(8);
|
||||
}
|
||||
|
||||
// The Default Toolkit must be set as soon as we know if we are going to use headless mode or not.
|
||||
// That is if the toolkit must be headless or not (GUI on/off). If we are running in headless mode
|
||||
// from this point, a HeadlessException will be thrown if we access a AWT/Swing component.
|
||||
// Read more about headless mode here:
|
||||
// http://java.sun.com/developer/technicalArticles/J2SE/Desktop/headless/
|
||||
Toolkit.getDefaultToolkit();
|
||||
}
|
||||
|
||||
private void changeDirectory(String robocodeDir) {
|
||||
try {
|
||||
FileUtil.setCwd(new File(robocodeDir));
|
||||
} catch (IOException e) {
|
||||
System.err.println(robocodeDir + " is not a valid directory to start Robocode in.");
|
||||
System.exit(8);
|
||||
}
|
||||
}
|
||||
|
||||
private void printUsage() {
|
||||
System.out.print(
|
||||
"Usage: robocode [-?] [-help] [-cwd path] [-battle filename [-results filename]\n"
|
||||
+ " [-record filename] [-recordXML filename] [-replay filename]\n"
|
||||
+ " [-tps tps] [-minimize] [-nodisplay] [-nosound]\n\n" + "where options include:\n"
|
||||
+ " -? or -help Prints out the command line usage of Robocode\n"
|
||||
+ " -cwd <path> Change the current working directory\n"
|
||||
+ " -battle <battle file> Run the battle specified in a battle file\n"
|
||||
+ " -results <results file> Save results to the specified text file\n"
|
||||
+ " -record <bin record file> Record the battle into the specified file as binary\n"
|
||||
+ " -recordXML <xml rec file> Record the battle into the specified file as XML\n"
|
||||
+ " -replay <record file> Replay the specified battle record\n"
|
||||
+ " -tps <tps> Set the TPS > 0 (Turns Per Second)\n"
|
||||
+ " -minimize Run minimized when Robocode starts\n"
|
||||
+ " -nodisplay Run with the display / GUI disabled\n"
|
||||
+ " -nosound Run with sound disabled\n\n" + "Java Properties include:\n"
|
||||
+ " -DWORKINGDIRECTORY=<path> Set the working directory\n"
|
||||
+ " -DROBOTPATH=<path> Set the robots directory (default is 'robots')\n"
|
||||
+ " -DBATTLEPATH=<path> Set the battles directory (default is 'battles')\n"
|
||||
+ " -DNOSECURITY=true|false Enable/disable Robocode's security manager\n"
|
||||
+ " -Ddebug=true|false Enable/disable debugging used for preventing\n"
|
||||
+ " robot timeouts and skipped turns, and allows an\n"
|
||||
+ " an unlimited painting buffer when debugging robots\n"
|
||||
+ " -DlogMessages=true|false Log messages and warnings will be disabled\n"
|
||||
+ " -DlogErrors=true|false Log errors will be disabled\n"
|
||||
+ " -DEXPERIMENTAL=true|false Enable/disable access to peer in robot interfaces\n"
|
||||
+ " -DPARALLEL=true|false Enable/disable parallel processing of robots turns\n"
|
||||
+ " -DRANDOMSEED=<long number> Set seed for deterministic behavior of random\n"
|
||||
+ " numbers\n");
|
||||
}
|
||||
|
||||
private void printResultsData(BattleCompletedEvent event) {
|
||||
// Do not print out if no result file has been specified and the GUI is enabled
|
||||
if ((setup.resultsFilename == null && (!setup.exitOnComplete || windowManager.isGUIEnabled()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
PrintStream out = null;
|
||||
FileOutputStream fos = null;
|
||||
|
||||
try {
|
||||
if (setup.resultsFilename == null) {
|
||||
out = Logger.realOut;
|
||||
} else {
|
||||
File f = new File(setup.resultsFilename);
|
||||
|
||||
try {
|
||||
fos = new FileOutputStream(f);
|
||||
out = new PrintStream(fos);
|
||||
} catch (IOException e) {
|
||||
Logger.logError(e);
|
||||
}
|
||||
}
|
||||
if (out != null) {
|
||||
BattleResultsTableModel resultsTable = new BattleResultsTableModel(event.getSortedResults(),
|
||||
event.getBattleRules().getNumRounds());
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
resultsTable.print(new PrintStream(baos));
|
||||
out.append(StringUtil.toBasicLatin(baos.toString()));
|
||||
}
|
||||
} finally {
|
||||
FileUtil.cleanupStream(out);
|
||||
FileUtil.cleanupStream(fos);
|
||||
}
|
||||
}
|
||||
|
||||
private class BattleObserver extends BattleAdaptor {
|
||||
boolean isReplay;
|
||||
|
||||
@Override
|
||||
public void onBattleStarted(BattleStartedEvent event) {
|
||||
isReplay = event.isReplay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBattleCompleted(BattleCompletedEvent event) {
|
||||
if (!isReplay) {
|
||||
printResultsData(event);
|
||||
}
|
||||
if (setup.recordFilename != null) {
|
||||
recordManager.saveRecord(setup.recordFilename, BattleRecordFormat.BINARY_ZIP,
|
||||
new SerializableOptions(false));
|
||||
}
|
||||
if (setup.recordXmlFilename != null) {
|
||||
recordManager.saveRecord(setup.recordXmlFilename, BattleRecordFormat.XML, new SerializableOptions(false));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBattleMessage(BattleMessageEvent event) {
|
||||
if (System.getProperty("logMessages", "true").equalsIgnoreCase("true")) {
|
||||
Logger.realOut.println(event.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBattleError(BattleErrorEvent event) {
|
||||
if (System.getProperty("logErrors", "true").equalsIgnoreCase("true")) {
|
||||
Logger.realErr.println(event.getError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
final IWindowManager windowManager = Container.getComponent(IWindowManager.class);
|
||||
|
||||
if (windowManager != null) {
|
||||
windowManager.cleanup();
|
||||
}
|
||||
Container.getComponent(IBattleManager.class).cleanup();
|
||||
Container.getComponent(IHostManager.class).cleanup();
|
||||
}
|
||||
|
||||
public void initForRobocodeEngine(IBattleListener listener) {
|
||||
final IWindowManager windowManager = Container.getComponent(IWindowManager.class);
|
||||
|
||||
if (windowManager != null) {
|
||||
windowManager.setSlave(true);
|
||||
windowManager.setEnableGUI(false);
|
||||
}
|
||||
Container.getComponent(IHostManager.class).initSecurity();
|
||||
if (listener != null) {
|
||||
Container.getComponent(IBattleManager.class).addListener(listener);
|
||||
}
|
||||
Container.getComponent(ICpuManager.class).getCpuConstant();
|
||||
Container.getComponent(IRepositoryManager.class).reload(versionManager.isLastRunVersionChanged());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.host;
|
||||
|
||||
|
||||
import net.sf.robocode.host.security.RobotClassLoader;
|
||||
import net.sf.robocode.host.proxies.*;
|
||||
import net.sf.robocode.peer.IRobotStatics;
|
||||
import net.sf.robocode.repository.IRobotItem;
|
||||
import net.sf.robocode.repository.RobotType;
|
||||
import static net.sf.robocode.io.Logger.logError;
|
||||
import net.sf.robocode.io.Logger;
|
||||
import net.sf.robocode.peer.IRobotPeer;
|
||||
import net.sf.robocode.security.HiddenAccess;
|
||||
import robocode.Droid;
|
||||
import robocode.Robot;
|
||||
import robocode.BorderSentry;
|
||||
import robocode.control.RobotSpecification;
|
||||
import robocode.robotinterfaces.*;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.*;
|
||||
import java.security.AccessControlException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
public class JavaHost implements IHost {
|
||||
public IRobotClassLoader createLoader(IRobotItem robotItem) {
|
||||
return new RobotClassLoader(robotItem.getClassPathURL(), robotItem.getFullClassName());
|
||||
}
|
||||
|
||||
public IHostingRobotProxy createRobotProxy(IHostManager hostManager, RobotSpecification robotSpecification, IRobotStatics statics, IRobotPeer peer) {
|
||||
IHostingRobotProxy robotProxy;
|
||||
final IRobotItem specification = (IRobotItem) HiddenAccess.getFileSpecification(robotSpecification);
|
||||
|
||||
if (specification.isTeamRobot()) {
|
||||
robotProxy = new TeamRobotProxy(specification, hostManager, peer, (RobotStatics) statics);
|
||||
} else if (specification.isAdvancedRobot()) {
|
||||
robotProxy = new AdvancedRobotProxy(specification, hostManager, peer, (RobotStatics) statics);
|
||||
} else if (specification.isStandardRobot()) {
|
||||
robotProxy = new StandardRobotProxy(specification, hostManager, peer, (RobotStatics) statics);
|
||||
} else if (specification.isJuniorRobot()) {
|
||||
robotProxy = new JuniorRobotProxy(specification, hostManager, peer, (RobotStatics) statics);
|
||||
} else {
|
||||
throw new AccessControlException("Unknown robot type");
|
||||
}
|
||||
return robotProxy;
|
||||
}
|
||||
|
||||
public String[] getReferencedClasses(IRobotItem robotItem) {
|
||||
IRobotClassLoader loader = null;
|
||||
|
||||
try {
|
||||
loader = createLoader(robotItem);
|
||||
loader.loadRobotMainClass(true);
|
||||
return loader.getReferencedClasses();
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
Logger.logError(e);
|
||||
return new String[0];
|
||||
} finally {
|
||||
if (loader != null) {
|
||||
loader.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RobotType getRobotType(IRobotItem robotItem, boolean resolve, boolean message) {
|
||||
IRobotClassLoader loader = null;
|
||||
|
||||
try {
|
||||
loader = createLoader(robotItem);
|
||||
Class<?> robotClass = loader.loadRobotMainClass(resolve);
|
||||
|
||||
if (robotClass == null || java.lang.reflect.Modifier.isAbstract(robotClass.getModifiers())) {
|
||||
// this class is not robot
|
||||
return RobotType.INVALID;
|
||||
}
|
||||
return checkInterfaces(robotClass, robotItem);
|
||||
|
||||
} catch (RuntimeException t) {
|
||||
if (message) {
|
||||
logError("Got an error with " + robotItem.getFullClassName() + ": " + t); // just message here
|
||||
if (t.getMessage() != null && t.getMessage().contains("Bad version number in .class file")) {
|
||||
logError(
|
||||
"Maybe robot was compiled with a newer Java version the Java version used for running Robocode?");
|
||||
}
|
||||
}
|
||||
return RobotType.INVALID;
|
||||
} finally {
|
||||
if (loader != null) {
|
||||
loader.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RobotType checkInterfaces(Class<?> robotClass, IRobotItem robotItem) {
|
||||
boolean isJuniorRobot = false;
|
||||
boolean isStandardRobot = false;
|
||||
boolean isInteractiveRobot = false;
|
||||
boolean isPaintRobot = false;
|
||||
boolean isAdvancedRobot = false;
|
||||
boolean isTeamRobot = false;
|
||||
boolean isDroid = false;
|
||||
boolean isSentryRobot = false;
|
||||
|
||||
if (IAdvancedRobot.class.isAssignableFrom(robotClass)) { // Note: must be checked first
|
||||
isAdvancedRobot = true;
|
||||
}
|
||||
if (Robot.class.isAssignableFrom(robotClass) && !isAdvancedRobot) {
|
||||
isStandardRobot = true;
|
||||
}
|
||||
if (IJuniorRobot.class.isAssignableFrom(robotClass)) { // Note: Must be checked before checking for standard robot
|
||||
isJuniorRobot = true;
|
||||
if (isAdvancedRobot) {
|
||||
throw new AccessControlException(
|
||||
robotItem.getFullClassName() + ": Junior robot should not implement IAdvancedRobot interface.");
|
||||
}
|
||||
}
|
||||
if (IBasicRobot.class.isAssignableFrom(robotClass)) {
|
||||
if (!(isAdvancedRobot || isJuniorRobot)) {
|
||||
isStandardRobot = true;
|
||||
}
|
||||
}
|
||||
if (ITeamRobot.class.isAssignableFrom(robotClass)) {
|
||||
isTeamRobot = true;
|
||||
}
|
||||
if (Droid.class.isAssignableFrom(robotClass)) {
|
||||
isDroid = true;
|
||||
}
|
||||
if (BorderSentry.class.isAssignableFrom(robotClass)) {
|
||||
isSentryRobot = true;
|
||||
}
|
||||
|
||||
if (IInteractiveRobot.class.isAssignableFrom(robotClass)) {
|
||||
// in this case we make sure that robot don't waste time
|
||||
if (checkMethodOverride(robotClass, Robot.class, "getInteractiveEventListener")
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onKeyPressed", KeyEvent.class)
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onKeyReleased", KeyEvent.class)
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onKeyTyped", KeyEvent.class)
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onMouseClicked", MouseEvent.class)
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onMouseEntered", MouseEvent.class)
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onMouseExited", MouseEvent.class)
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onMousePressed", MouseEvent.class)
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onMouseReleased", MouseEvent.class)
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onMouseMoved", MouseEvent.class)
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onMouseDragged", MouseEvent.class)
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onMouseWheelMoved", MouseWheelEvent.class)
|
||||
) {
|
||||
isInteractiveRobot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IPaintRobot.class.isAssignableFrom(robotClass)) {
|
||||
if (checkMethodOverride(robotClass, Robot.class, "getPaintEventListener")
|
||||
|| checkMethodOverride(robotClass, Robot.class, "onPaint", Graphics2D.class)
|
||||
) {
|
||||
isPaintRobot = true;
|
||||
}
|
||||
}
|
||||
|
||||
return new RobotType(isJuniorRobot, isStandardRobot, isInteractiveRobot, isPaintRobot, isAdvancedRobot,
|
||||
isTeamRobot, isDroid, isSentryRobot);
|
||||
}
|
||||
|
||||
private boolean checkMethodOverride(Class<?> robotClass, Class<?> knownBase, String name, Class<?>... parameterTypes) {
|
||||
if (knownBase.isAssignableFrom(robotClass)) {
|
||||
final Method getInteractiveEventListener;
|
||||
|
||||
try {
|
||||
getInteractiveEventListener = robotClass.getMethod(name, parameterTypes);
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
if (getInteractiveEventListener.getDeclaringClass().equals(knownBase)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,572 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.host.events;
|
||||
|
||||
|
||||
import net.sf.robocode.host.proxies.BasicRobotProxy;
|
||||
import net.sf.robocode.security.HiddenAccess;
|
||||
import robocode.*;
|
||||
import robocode.exception.EventInterruptedException;
|
||||
import robocode.robotinterfaces.IBasicRobot;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
||||
// XXX Remember to update the .NET version whenever a change is made to this class!
|
||||
|
||||
/**
|
||||
* This class is used for managing the event queue for a robot.
|
||||
*
|
||||
* @author Mathew A. Nelson (original)
|
||||
* @author Flemming N. Larsen (contributor)
|
||||
* @author Matthew Reeder (contributor)
|
||||
* @author Robert D. Maupin (contributor)
|
||||
* @author Nathaniel Troutman (contributor)
|
||||
* @author Pavel Savara (contributor)
|
||||
*/
|
||||
public final class EventManager implements IEventManager {
|
||||
|
||||
private final static int MAX_PRIORITY = 100;
|
||||
public final static int MAX_EVENT_STACK = 2;
|
||||
public final static int MAX_QUEUE_SIZE = 256;
|
||||
|
||||
private final List<Condition> customEvents = new CopyOnWriteArrayList<Condition>();
|
||||
private final EventQueue eventQueue;
|
||||
|
||||
private final boolean[] interruptible = new boolean[MAX_PRIORITY + 1];
|
||||
private Event currentTopEvent;
|
||||
private int currentTopEventPriority;
|
||||
private ScannedRobotEvent dummyScannedRobotEvent;
|
||||
private Map<String, Event> eventNames;
|
||||
|
||||
private IBasicRobot robot;
|
||||
private BasicRobotProxy robotProxy;
|
||||
|
||||
/**
|
||||
* Constructs a new EventManager.
|
||||
*
|
||||
* @param robotProxy the robot proxy that this event manager applies to.
|
||||
*/
|
||||
public EventManager(BasicRobotProxy robotProxy) {
|
||||
this.robotProxy = robotProxy;
|
||||
eventQueue = new EventQueue();
|
||||
|
||||
registerEventNames();
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event to the event queue.
|
||||
* @param event is the event to add to the event queue.
|
||||
*/
|
||||
public void add(Event event) {
|
||||
if (!HiddenAccess.isCriticalEvent(event)) {
|
||||
final int priority = getEventPriority(event.getClass().getName());
|
||||
HiddenAccess.setEventPriority(event, priority);
|
||||
}
|
||||
addImpl(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for adding an event to the event queue.
|
||||
* @param event is the event to add to the event queue.
|
||||
*/
|
||||
private void addImpl(Event event) {
|
||||
if (eventQueue != null) {
|
||||
if (eventQueue.size() > MAX_QUEUE_SIZE) {
|
||||
robotProxy.println(
|
||||
"Not adding to " + robotProxy.getStatics().getName() + "'s queue, exceeded " + MAX_QUEUE_SIZE
|
||||
+ " events in queue.");
|
||||
} else {
|
||||
HiddenAccess.setEventTime(event, getTime());
|
||||
eventQueue.add(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an custom event to the event queue based on a condition.
|
||||
* @param condition is the condition that must be met in order to trigger the custom event.
|
||||
*/
|
||||
public void addCustomEvent(Condition condition) {
|
||||
customEvents.add(condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all events from the event queue.
|
||||
* @param includingSystemEvents {@code true} if system events must be removed as well;
|
||||
* {@code false} if system events should stay on the event queue.
|
||||
*/
|
||||
public void clearAllEvents(boolean includingSystemEvents) {
|
||||
eventQueue.clear(includingSystemEvents);
|
||||
// customEvents.clear(); // Custom event should not be cleared here
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the event queue.
|
||||
* <p>
|
||||
* This method should be called when the event queue is no longer needed,
|
||||
* i.e. before it must be garbage collected.
|
||||
*/
|
||||
public void cleanup() {
|
||||
// Remove all events
|
||||
reset();
|
||||
|
||||
// Remove all references to robots
|
||||
robot = null;
|
||||
robotProxy = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all events currently in the robot's queue.
|
||||
*/
|
||||
public List<Event> getAllEvents() {
|
||||
List<Event> events = new ArrayList<Event>();
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
events.add(e);
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all BulletHitBulletEvents currently in the robot's queue.
|
||||
*/
|
||||
public List<BulletHitBulletEvent> getBulletHitBulletEvents() {
|
||||
List<BulletHitBulletEvent> events = new ArrayList<BulletHitBulletEvent>();
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
if (e instanceof BulletHitBulletEvent) {
|
||||
events.add((BulletHitBulletEvent) e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all BulletHitEvents currently in the robot's queue.
|
||||
*/
|
||||
public List<BulletHitEvent> getBulletHitEvents() {
|
||||
List<BulletHitEvent> events = new ArrayList<BulletHitEvent>();
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
if (e instanceof BulletHitEvent) {
|
||||
events.add((BulletHitEvent) e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all BulletMissedEvents currently in the robot's queue.
|
||||
*/
|
||||
public List<BulletMissedEvent> getBulletMissedEvents() {
|
||||
List<BulletMissedEvent> events = new ArrayList<BulletMissedEvent>();
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
if (e instanceof BulletMissedEvent) {
|
||||
events.add((BulletMissedEvent) e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all HitByBulletEvents currently in the robot's queue.
|
||||
*/
|
||||
public List<HitByBulletEvent> getHitByBulletEvents() {
|
||||
List<HitByBulletEvent> events = new ArrayList<HitByBulletEvent>();
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
if (e instanceof HitByBulletEvent) {
|
||||
events.add((HitByBulletEvent) e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all HitRobotEvents currently in the robot's queue.
|
||||
*/
|
||||
public List<HitRobotEvent> getHitRobotEvents() {
|
||||
List<HitRobotEvent> events = new ArrayList<HitRobotEvent>();
|
||||
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
if (e instanceof HitRobotEvent) {
|
||||
events.add((HitRobotEvent) e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all HitWallEvents currently in the robot's queue.
|
||||
*/
|
||||
public List<HitWallEvent> getHitWallEvents() {
|
||||
List<HitWallEvent> events = new ArrayList<HitWallEvent>();
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
if (e instanceof HitWallEvent) {
|
||||
events.add((HitWallEvent) e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all RobotDeathEvents currently in the robot's queue.
|
||||
*/
|
||||
public List<RobotDeathEvent> getRobotDeathEvents() {
|
||||
List<RobotDeathEvent> events = new ArrayList<RobotDeathEvent>();
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
if (e instanceof RobotDeathEvent) {
|
||||
events.add((RobotDeathEvent) e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all ScannedRobotEvents currently in the robot's queue.
|
||||
*/
|
||||
public List<ScannedRobotEvent> getScannedRobotEvents() {
|
||||
List<ScannedRobotEvent> events = new ArrayList<ScannedRobotEvent>();
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
if (e instanceof ScannedRobotEvent) {
|
||||
events.add((ScannedRobotEvent) e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all MessageEvents currently in the robot's queue.
|
||||
*/
|
||||
public List<MessageEvent> getMessageEvents() {
|
||||
List<MessageEvent> events = new ArrayList<MessageEvent>();
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
if (e instanceof MessageEvent) {
|
||||
events.add((MessageEvent) e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all StatusEvents currently in the robot's queue.
|
||||
*/
|
||||
public List<StatusEvent> getStatusEvents() {
|
||||
List<StatusEvent> events = new ArrayList<StatusEvent>();
|
||||
synchronized (eventQueue) {
|
||||
for (Event e : eventQueue) {
|
||||
if (e instanceof StatusEvent) {
|
||||
events.add((StatusEvent) e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the priority of the current top event.
|
||||
*/
|
||||
public int getCurrentTopEventPriority() {
|
||||
return currentTopEventPriority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current top event.
|
||||
*/
|
||||
public Event getCurrentTopEvent() {
|
||||
return currentTopEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if events with a specific event priority are interruptible.
|
||||
* @param priority is the event priority that must be checked.
|
||||
* @see #setInterruptible(int, boolean)
|
||||
*/
|
||||
public boolean isInterruptible(int priority) {
|
||||
return interruptible[priority];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the robot that will receive events dispatched from the event queue.
|
||||
* @param robot is the robot that will receive event dispatched from the event queue.
|
||||
*/
|
||||
public void setRobot(IBasicRobot robot) {
|
||||
this.robot = robot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the priority of a ScannedRobotEvent.
|
||||
*/
|
||||
public int getScannedRobotEventPriority() {
|
||||
return dummyScannedRobotEvent.getPriority();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time/turn of the battle round.
|
||||
*/
|
||||
public long getTime() {
|
||||
return robotProxy.getTimeImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the heart of the event manager, which processes the events for a robot.
|
||||
*/
|
||||
public void processEvents() {
|
||||
// Remove old events
|
||||
eventQueue.clear(getTime() - MAX_EVENT_STACK);
|
||||
|
||||
// Process custom events
|
||||
for (Condition customEvent : customEvents) {
|
||||
boolean conditionSatisfied = callUserCode(customEvent);
|
||||
if (conditionSatisfied) {
|
||||
addImpl(new CustomEvent(customEvent));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the events based on the time and priority of the events
|
||||
eventQueue.sort();
|
||||
|
||||
// Process event queue here
|
||||
Event currentEvent;
|
||||
while ((currentEvent = (eventQueue.size() > 0) ? eventQueue.get(0) : null) != null
|
||||
&& currentEvent.getPriority() >= currentTopEventPriority) {
|
||||
|
||||
if (currentEvent.getPriority() == currentTopEventPriority) {
|
||||
if (currentTopEventPriority > Integer.MIN_VALUE && isInterruptible(currentTopEventPriority)) {
|
||||
setInterruptible(currentTopEventPriority, false); // we're going to restart it, so reset.
|
||||
|
||||
// We are already in an event handler, took action, and a new event was generated.
|
||||
// So we want to break out of the old handler to process it here.
|
||||
throw new EventInterruptedException(currentEvent.getPriority());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int oldTopEventPriority = currentTopEventPriority;
|
||||
|
||||
currentTopEventPriority = currentEvent.getPriority();
|
||||
currentTopEvent = currentEvent;
|
||||
|
||||
eventQueue.remove(currentEvent);
|
||||
try {
|
||||
dispatch(currentEvent);
|
||||
|
||||
setInterruptible(currentTopEventPriority, false);
|
||||
|
||||
} catch (EventInterruptedException e) {
|
||||
currentTopEvent = null;
|
||||
} catch (RuntimeException e) {
|
||||
currentTopEvent = null;
|
||||
throw e;
|
||||
} catch (MyException e) {
|
||||
currentTopEvent = null;
|
||||
throw e;
|
||||
} finally {
|
||||
currentTopEventPriority = oldTopEventPriority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user's condition for a custom event is satisfied.
|
||||
* @param condition is the condition to check.
|
||||
* @return {@code true} if the condition is satisfied; {@code false} otherwise.
|
||||
*/
|
||||
private boolean callUserCode(Condition condition) {
|
||||
boolean conditionSatisfied;
|
||||
robotProxy.setTestingCondition(true);
|
||||
try {
|
||||
conditionSatisfied = condition.test();
|
||||
} finally {
|
||||
robotProxy.setTestingCondition(false);
|
||||
}
|
||||
return conditionSatisfied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an event for a robot.
|
||||
* <p>
|
||||
* Too old events will not be dispatched and a critical event is always dispatched.
|
||||
*
|
||||
* @param event the event to dispatch to the robot.
|
||||
*/
|
||||
private void dispatch(Event event) {
|
||||
if (robot != null && event != null) {
|
||||
try {
|
||||
// skip too old events
|
||||
if ((event.getTime() > getTime() - MAX_EVENT_STACK) || HiddenAccess.isCriticalEvent(event)) {
|
||||
HiddenAccess.dispatch(event, robot, robotProxy.getStatics(), robotProxy.getGraphicsImpl());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
robotProxy.println("SYSTEM: " + ex.getClass().getName() + " occurred on " + event.getClass().getName());
|
||||
ex.printStackTrace(robotProxy.getOut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the custom event with the specified condition from the event queue.
|
||||
* @param condition is the condition of the custom event to remove.
|
||||
*/
|
||||
public void removeCustomEvent(Condition condition) {
|
||||
customEvents.remove(condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all custom events from the event queue.
|
||||
*/
|
||||
public void resetCustomEvents() {
|
||||
customEvents.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this event manager by removing all events from the event queue.
|
||||
*/
|
||||
public synchronized void reset() {
|
||||
currentTopEventPriority = Integer.MIN_VALUE;
|
||||
clearAllEvents(true);
|
||||
customEvents.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the interruptible flag for events with a specific priority.
|
||||
* When an event is interrupted, events with the same priority are allowed to restart the event handler.
|
||||
*
|
||||
* @param priority is the priority of the event to set the interruptible flag for.
|
||||
* @param isInterruptable {@code true} if events with the specified priority must be interruptible
|
||||
* allowing events with the same priority to restart the event handler.
|
||||
* {@code false} if events with the specified priority must not be interruptible
|
||||
* disallowing events with the same priority to restart the event handler.
|
||||
*/
|
||||
public void setInterruptible(int priority, boolean isInterruptable) {
|
||||
if (priority >= 0 && priority < MAX_PRIORITY) {
|
||||
interruptible[priority] = isInterruptable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the priority of events belonging to a specific class.
|
||||
* @param eventClass is a string with the full class name of the event type to get the priority from.
|
||||
* @return the event priority of the specified event class.
|
||||
* @see robocode.Event#getPriority()
|
||||
*/
|
||||
public int getEventPriority(String eventClass) {
|
||||
if (eventClass == null) {
|
||||
return -1;
|
||||
}
|
||||
final Event event = eventNames.get(eventClass);
|
||||
if (event == null) {
|
||||
return -1;
|
||||
}
|
||||
return event.getPriority();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event priority of events belonging to a specific class.
|
||||
* @param eventClass is a string with the full class name of the event type to set the priority for.
|
||||
* @param priority is the new priority
|
||||
*/
|
||||
public void setEventPriority(String eventClass, int priority) {
|
||||
if (eventClass == null) {
|
||||
return;
|
||||
}
|
||||
final Event event = eventNames.get(eventClass);
|
||||
if (event == null) {
|
||||
robotProxy.println("SYSTEM: Unknown event class: " + eventClass);
|
||||
return;
|
||||
}
|
||||
if (HiddenAccess.isCriticalEvent(event)) {
|
||||
robotProxy.println("SYSTEM: You may not change the priority of a system event.");
|
||||
}
|
||||
HiddenAccess.setEventPriority(event, priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the full and simple class names of all events used by {@link #getEventPriority(String)} and
|
||||
* {@link #setEventPriority(String, int)} and sets the default priority of each event class.
|
||||
*/
|
||||
private void registerEventNames() {
|
||||
eventNames = new HashMap<String, Event>();
|
||||
dummyScannedRobotEvent = new ScannedRobotEvent(null, 0, 0, 0, 0, 0, false);
|
||||
registerEventNames(new BattleEndedEvent(false, null));
|
||||
registerEventNames(new BulletHitBulletEvent(null, null));
|
||||
registerEventNames(new BulletHitEvent(null, 0, null));
|
||||
registerEventNames(new BulletMissedEvent(null));
|
||||
registerEventNames(new DeathEvent());
|
||||
registerEventNames(new HitByBulletEvent(0, null));
|
||||
registerEventNames(new HitRobotEvent(null, 0, 0, false));
|
||||
registerEventNames(new HitWallEvent(0));
|
||||
registerEventNames(new KeyPressedEvent(null));
|
||||
registerEventNames(new KeyReleasedEvent(null));
|
||||
registerEventNames(new KeyTypedEvent(null));
|
||||
registerEventNames(new MessageEvent(null, null));
|
||||
registerEventNames(new MouseClickedEvent(null));
|
||||
registerEventNames(new MouseDraggedEvent(null));
|
||||
registerEventNames(new MouseEnteredEvent(null));
|
||||
registerEventNames(new MouseExitedEvent(null));
|
||||
registerEventNames(new MouseMovedEvent(null));
|
||||
registerEventNames(new MousePressedEvent(null));
|
||||
registerEventNames(new MouseReleasedEvent(null));
|
||||
registerEventNames(new MouseWheelMovedEvent(null));
|
||||
registerEventNames(new PaintEvent());
|
||||
registerEventNames(new RobotDeathEvent(null));
|
||||
registerEventNames(new RoundEndedEvent(0, 0, 0));
|
||||
registerEventNames(dummyScannedRobotEvent);
|
||||
registerEventNames(new SkippedTurnEvent(0));
|
||||
registerEventNames(new StatusEvent(null));
|
||||
registerEventNames(new WinEvent());
|
||||
|
||||
// same as any line above but for custom event
|
||||
final DummyCustomEvent customEvent = new DummyCustomEvent();
|
||||
eventNames.put("robocode.CustomEvent", customEvent); // full name with package name
|
||||
eventNames.put("CustomEvent", customEvent); // only the class name
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the full and simple class name of the specified event and sets the default
|
||||
* priority of the event class.
|
||||
* @param event an event belonging to the event class to register the class name for etc.
|
||||
*/
|
||||
private void registerEventNames(Event event) {
|
||||
if (!HiddenAccess.isCriticalEvent(event)) {
|
||||
HiddenAccess.setDefaultPriority(event);
|
||||
}
|
||||
final Class<?> type = event.getClass();
|
||||
eventNames.put(type.getName(), event); // full name with package name
|
||||
eventNames.put(type.getSimpleName(), event); // only the class name
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy CustomEvent used only for registering the class name.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static final class DummyCustomEvent extends CustomEvent {
|
||||
public DummyCustomEvent() {
|
||||
super(null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.host.proxies;
|
||||
|
||||
|
||||
import net.sf.robocode.host.events.EventManager;
|
||||
import net.sf.robocode.host.io.RobotFileSystemManager;
|
||||
import net.sf.robocode.host.io.RobotOutputStream;
|
||||
import net.sf.robocode.host.security.RobotThreadManager;
|
||||
import net.sf.robocode.host.*;
|
||||
import static net.sf.robocode.io.Logger.logError;
|
||||
import static net.sf.robocode.io.Logger.logMessage;
|
||||
import net.sf.robocode.peer.BadBehavior;
|
||||
import net.sf.robocode.peer.ExecCommands;
|
||||
import net.sf.robocode.peer.IRobotPeer;
|
||||
import net.sf.robocode.repository.IRobotItem;
|
||||
import net.sf.robocode.core.Container;
|
||||
import robocode.RobotStatus;
|
||||
import robocode.exception.AbortedException;
|
||||
import robocode.exception.DeathException;
|
||||
import robocode.exception.DisabledException;
|
||||
import robocode.exception.WinException;
|
||||
import robocode.robotinterfaces.IBasicRobot;
|
||||
import robocode.robotinterfaces.peer.IBasicRobotPeer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
// XXX Remember to update the .NET version whenever a change is made to this class!
|
||||
|
||||
/**
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
abstract class HostingRobotProxy implements IHostingRobotProxy, IHostedThread {
|
||||
|
||||
private final IRobotItem robotSpecification;
|
||||
|
||||
protected EventManager eventManager;
|
||||
private final IHostManager hostManager;
|
||||
protected RobotThreadManager robotThreadManager;
|
||||
protected RobotFileSystemManager robotFileSystemManager;
|
||||
private IThreadManager threadManager;
|
||||
|
||||
private IBasicRobot robot;
|
||||
protected final IRobotPeer peer;
|
||||
protected IRobotClassLoader robotClassLoader;
|
||||
|
||||
protected final RobotStatics statics;
|
||||
protected RobotOutputStream out;
|
||||
|
||||
private final Set<String> securityViolations = Collections.synchronizedSet(new HashSet<String>());
|
||||
|
||||
HostingRobotProxy(IRobotItem robotSpecification, IHostManager hostManager, IRobotPeer peer, RobotStatics statics) {
|
||||
this.peer = peer;
|
||||
this.statics = statics;
|
||||
this.hostManager = hostManager;
|
||||
this.robotSpecification = robotSpecification;
|
||||
|
||||
robotClassLoader = getHost(robotSpecification).createLoader(robotSpecification);
|
||||
robotClassLoader.setRobotProxy(this);
|
||||
|
||||
out = new RobotOutputStream();
|
||||
robotThreadManager = new RobotThreadManager(this);
|
||||
|
||||
loadClassBattle();
|
||||
|
||||
robotFileSystemManager = new RobotFileSystemManager(this, hostManager.getRobotFilesystemQuota(),
|
||||
robotSpecification.getWritableDirectory(), robotSpecification.getReadableDirectory(),
|
||||
robotSpecification.getRootPath());
|
||||
|
||||
robotFileSystemManager.initialize();
|
||||
}
|
||||
|
||||
private JavaHost getHost(IRobotItem robotSpecification) {
|
||||
return (JavaHost) Container.cache.getComponent("robocode.host." + robotSpecification.getPlatform().toLowerCase());
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
robot = null;
|
||||
|
||||
// Remove the file system and the manager
|
||||
robotFileSystemManager = null;
|
||||
if (out != null) {
|
||||
out.close();
|
||||
out = null;
|
||||
}
|
||||
|
||||
if (robotThreadManager != null) {
|
||||
robotThreadManager.cleanup();
|
||||
}
|
||||
robotThreadManager = null;
|
||||
|
||||
// Cleanup and remove class manager
|
||||
if (robotClassLoader != null) {
|
||||
robotClassLoader.cleanup();
|
||||
robotClassLoader = null;
|
||||
}
|
||||
}
|
||||
|
||||
public RobotOutputStream getOut() {
|
||||
return out;
|
||||
}
|
||||
|
||||
public void println(String s) {
|
||||
out.println(s);
|
||||
}
|
||||
|
||||
private void println(Throwable ex) {
|
||||
ex.printStackTrace(out);
|
||||
}
|
||||
|
||||
public RobotStatics getStatics() {
|
||||
return statics;
|
||||
}
|
||||
|
||||
public RobotFileSystemManager getRobotFileSystemManager() {
|
||||
return robotFileSystemManager;
|
||||
}
|
||||
|
||||
public ClassLoader getRobotClassloader() {
|
||||
return (ClassLoader) robotClassLoader;
|
||||
}
|
||||
|
||||
// -----------
|
||||
// battle driven methods
|
||||
// -----------
|
||||
|
||||
protected abstract void initializeRound(ExecCommands commands, RobotStatus status);
|
||||
|
||||
public void startRound(ExecCommands commands, RobotStatus status) {
|
||||
initializeRound(commands, status);
|
||||
threadManager = ((HostManager) hostManager).getThreadManager();
|
||||
robotThreadManager.start(threadManager);
|
||||
}
|
||||
|
||||
public void forceStopThread() {
|
||||
if (!robotThreadManager.forceStop()) {
|
||||
peer.punishBadBehavior(BadBehavior.UNSTOPPABLE);
|
||||
peer.setRunning(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void waitForStopThread() {
|
||||
if (!robotThreadManager.waitForStop()) {
|
||||
peer.punishBadBehavior(BadBehavior.UNSTOPPABLE);
|
||||
peer.setRunning(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadClassBattle() {
|
||||
try {
|
||||
robotClassLoader.loadRobotMainClass(true);
|
||||
} catch (RuntimeException e) {
|
||||
println("SYSTEM: Could not load " + statics.getName() + " : ");
|
||||
println(e);
|
||||
drainEnergy();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean loadRobotRound() {
|
||||
robot = null;
|
||||
try {
|
||||
threadManager.setLoadingRobot(this);
|
||||
robot = robotClassLoader.createRobotInstance();
|
||||
if (robot == null) {
|
||||
println("SYSTEM: Skipping robot: " + statics.getName());
|
||||
return false;
|
||||
}
|
||||
robot.setOut(out);
|
||||
robot.setPeer((IBasicRobotPeer) this);
|
||||
eventManager.setRobot(robot);
|
||||
} catch (IllegalAccessException e) {
|
||||
println("SYSTEM: Unable to instantiate this robot: " + e);
|
||||
println("SYSTEM: Is your constructor marked public?");
|
||||
println(e);
|
||||
robot = null;
|
||||
logError(e);
|
||||
return false;
|
||||
} catch (RuntimeException e) {
|
||||
println("SYSTEM: An error occurred during initialization of " + statics.getName());
|
||||
println("SYSTEM: " + e);
|
||||
println(e);
|
||||
robot = null;
|
||||
logError(e);
|
||||
return false;
|
||||
} finally {
|
||||
threadManager.setLoadingRobot(null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract void executeImpl();
|
||||
|
||||
public void run() {
|
||||
// Only initialize AWT if we are not running in headless mode.
|
||||
// Bugfix [2833271] IllegalThreadStateException with the AWT-Shutdown thread.
|
||||
// Read more about headless mode here:
|
||||
// http://java.sun.com/developer/technicalArticles/J2SE/Desktop/headless/
|
||||
if (System.getProperty("java.awt.headless", "true").equals("false")) {
|
||||
robotThreadManager.initAWT();
|
||||
}
|
||||
|
||||
if (robotSpecification.isValid() && loadRobotRound()) {
|
||||
try {
|
||||
if (robot != null) {
|
||||
peer.setRunning(true);
|
||||
|
||||
// Process all events for the first turn.
|
||||
// This is done as the first robot status event must occur before the robot
|
||||
// has started running.
|
||||
eventManager.processEvents();
|
||||
|
||||
// Call user code
|
||||
callUserCode();
|
||||
}
|
||||
while (peer.isRunning()) {
|
||||
executeImpl();
|
||||
}
|
||||
} catch (WinException e) {// Do nothing
|
||||
} catch (AbortedException e) {// Do nothing
|
||||
} catch (DeathException e) {
|
||||
println("SYSTEM: " + statics.getName() + " has died");
|
||||
} catch (DisabledException e) {
|
||||
drainEnergy();
|
||||
|
||||
String msg = e.getMessage();
|
||||
if (msg == null) {
|
||||
msg = "";
|
||||
} else {
|
||||
msg = ": " + msg;
|
||||
}
|
||||
println("SYSTEM: Robot disabled: " + msg);
|
||||
logMessage("Robot disabled: " + statics.getName());
|
||||
} catch (Exception e) {
|
||||
drainEnergy();
|
||||
println(e);
|
||||
logMessage(statics.getName() + ": Exception: " + e); // without stack here
|
||||
} catch (ThreadDeath e) {
|
||||
drainEnergy();
|
||||
println(e);
|
||||
logMessage(statics.getName() + " stopped successfully.");
|
||||
throw e; // must be re-thrown in order to stop the thread
|
||||
} catch (RuntimeException t) {
|
||||
drainEnergy();
|
||||
println(t);
|
||||
logMessage(statics.getName() + ": Throwable: " + t); // without stack here
|
||||
} finally {
|
||||
waitForBattleEndImpl();
|
||||
}
|
||||
} else {
|
||||
drainEnergy();
|
||||
peer.punishBadBehavior(BadBehavior.CANNOT_START);
|
||||
waitForBattleEndImpl();
|
||||
}
|
||||
|
||||
peer.setRunning(false);
|
||||
|
||||
// If battle is waiting for us, well, all done!
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void callUserCode() {
|
||||
Runnable runnable = robot.getRobotRunnable();
|
||||
if (runnable != null) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void waitForBattleEndImpl();
|
||||
|
||||
public void drainEnergy() {
|
||||
peer.drainEnergy();
|
||||
}
|
||||
|
||||
public void punishSecurityViolation(String message) {
|
||||
// Prevent unit tests of failing if multiple threads are calling this method in the same time.
|
||||
// We only want the a specific type of security violation logged once so we only get one error
|
||||
// per security violation.
|
||||
synchronized (securityViolations) {
|
||||
String key = message;
|
||||
|
||||
if (key.startsWith("Preventing Thread-")) {
|
||||
key = key.replaceAll("\\d+", "X");
|
||||
}
|
||||
if (!securityViolations.contains(key)) {
|
||||
securityViolations.add(key);
|
||||
logError(message);
|
||||
println("SYSTEM: " + message);
|
||||
|
||||
if (securityViolations.size() == 1) {
|
||||
peer.drainEnergy();
|
||||
peer.punishBadBehavior(BadBehavior.SECURITY_VIOLATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,424 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.host.security;
|
||||
|
||||
|
||||
import net.sf.robocode.core.Container;
|
||||
import net.sf.robocode.host.IHostedThread;
|
||||
import net.sf.robocode.host.IRobotClassLoader;
|
||||
import net.sf.robocode.io.FileUtil;
|
||||
import net.sf.robocode.io.Logger;
|
||||
import net.sf.robocode.io.RobocodeProperties;
|
||||
import net.sf.robocode.io.URLJarCollector;
|
||||
import robocode.robotinterfaces.IBasicRobot;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* This class loader is used by robots. It isolates classes which belong to robot and load them locally.
|
||||
* General java classes or robocode.api classes are loaded by parent loader and shared with Robocode engine.
|
||||
* Attempts to load classes of Robocode engine are blocked.
|
||||
*
|
||||
* @author Mathew A. Nelson (original)
|
||||
* @author Flemming N. Larsen (contributor)
|
||||
* @author Matthew Reeder (contributor)
|
||||
* @author Robert D. Maupin (contributor)
|
||||
* @author Nathaniel Troutman (contributor)
|
||||
*/
|
||||
public class RobotClassLoader extends URLClassLoader implements IRobotClassLoader {
|
||||
|
||||
static final String UNTRUSTED_URL = "http://robocode.sf.net/untrusted";
|
||||
|
||||
private static final PermissionCollection EMPTY_PERMISSIONS = new Permissions();
|
||||
|
||||
private final String fullClassName;
|
||||
|
||||
private ClassLoader parent;
|
||||
private CodeSource codeSource;
|
||||
|
||||
private IHostedThread robotProxy;
|
||||
private Class<?> robotClass;
|
||||
|
||||
// Names on classes referenced from the robot class
|
||||
private Set<String> referencedClasses = new HashSet<String>();
|
||||
|
||||
// Cached names on found system classes
|
||||
private Set<String> foundSystemClasses = new HashSet<String>();
|
||||
|
||||
// Cached warning messages
|
||||
private String[] staticRobotInstanceWarning;
|
||||
|
||||
public RobotClassLoader(URL robotClassPath, String robotFullClassName) {
|
||||
super(new URL[] { robotClassPath}, Container.systemLoader);
|
||||
fullClassName = robotFullClassName;
|
||||
parent = getParent();
|
||||
try {
|
||||
codeSource = new CodeSource(new URL(UNTRUSTED_URL), (Certificate[]) null);
|
||||
} catch (MalformedURLException ignored) {}
|
||||
}
|
||||
|
||||
public void setRobotProxy(Object robotProxy) {
|
||||
this.robotProxy = (IHostedThread) robotProxy;
|
||||
}
|
||||
|
||||
public synchronized void addURL(URL url) {
|
||||
super.addURL(url);
|
||||
}
|
||||
|
||||
public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
if (name.startsWith("java.lang")) {
|
||||
// we always delegate java.lang stuff to parent loader
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
if (RobocodeProperties.isSecurityOn()) {
|
||||
testPackages(name);
|
||||
}
|
||||
if (!name.startsWith("robocode")) {
|
||||
Class<?> result = loadRobotClassLocaly(name, resolve);
|
||||
if (result != null) {
|
||||
// yes, it is in robot's class path
|
||||
// we loaded it locally
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// it is robot API
|
||||
// or java class
|
||||
// or security is off
|
||||
// so we delegate to parent class loader
|
||||
return parent.loadClass(name);
|
||||
}
|
||||
|
||||
private void testPackages(String name) throws ClassNotFoundException {
|
||||
if (name.startsWith("net.sf.robocode")) {
|
||||
String message = "Robots are not allowed to reference Robocode engine in package: net.sf.robocode";
|
||||
|
||||
punishSecurityViolation(message);
|
||||
throw new ClassNotFoundException(message);
|
||||
}
|
||||
if (name.startsWith("robocode.control")) {
|
||||
String message = "Robots are not allowed to reference Robocode engine in package: robocode.control";
|
||||
|
||||
punishSecurityViolation(message);
|
||||
throw new ClassNotFoundException(message);
|
||||
}
|
||||
if (RobocodeProperties.isSecurityOn() && name.startsWith("javax.swing")) {
|
||||
String message = "Robots are not allowed to reference Robocode engine in package: javax.swing";
|
||||
|
||||
punishSecurityViolation(message);
|
||||
throw new ClassNotFoundException(message);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> loadRobotClassLocaly(String name, boolean resolve) throws ClassNotFoundException {
|
||||
Class<?> result = findLoadedClass(name);
|
||||
if (result == null) {
|
||||
ByteBuffer resource = findLocalResource(name);
|
||||
if (resource != null) {
|
||||
result = defineClass(name, resource, codeSource);
|
||||
if (resolve) {
|
||||
resolveClass(result);
|
||||
}
|
||||
ClassAnalyzer.getReferencedClasses(resource, referencedClasses);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// this whole fun is there to be able to provide defineClass with bytes
|
||||
// we need to call defineClass to be able to set codeSource to untrustedLocation
|
||||
private ByteBuffer findLocalResource(final String name) {
|
||||
return AccessController.doPrivileged(new PrivilegedAction<ByteBuffer>() {
|
||||
public ByteBuffer run() {
|
||||
// try to find it in robot's class path
|
||||
// this is URL, don't change to File.pathSeparator
|
||||
String path = name.replace('.', '/').concat(".class");
|
||||
URL url = findResource(path);
|
||||
ByteBuffer result = null;
|
||||
InputStream is = null;
|
||||
BufferedInputStream bis = null;
|
||||
|
||||
if (url != null) {
|
||||
try {
|
||||
URLConnection connection = URLJarCollector.openConnection(url);
|
||||
|
||||
is = connection.getInputStream();
|
||||
bis = new BufferedInputStream(is);
|
||||
|
||||
result = ByteBuffer.allocate(1024 * 8);
|
||||
boolean done = false;
|
||||
|
||||
do {
|
||||
do {
|
||||
int res = bis.read(result.array(), result.position(), result.remaining());
|
||||
|
||||
if (res == -1) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
result.position(result.position() + res);
|
||||
} while (result.remaining() != 0);
|
||||
result.flip();
|
||||
if (!done) {
|
||||
result = ByteBuffer.allocate(result.capacity() * 2).put(result);
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
} catch (IOException e) {
|
||||
Logger.logError(e);
|
||||
return null;
|
||||
} finally {
|
||||
FileUtil.cleanupStream(bis);
|
||||
FileUtil.cleanupStream(is);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void punishSecurityViolation(String message) {
|
||||
if (robotProxy != null) {
|
||||
robotProxy.punishSecurityViolation(message);
|
||||
}
|
||||
}
|
||||
|
||||
protected PermissionCollection getPermissions(CodeSource codesource) {
|
||||
if (RobocodeProperties.isSecurityOn()) {
|
||||
return EMPTY_PERMISSIONS;
|
||||
}
|
||||
return super.getPermissions(codesource);
|
||||
}
|
||||
|
||||
public String[] getReferencedClasses() {
|
||||
return referencedClasses.toArray(new String[referencedClasses.size()]);
|
||||
}
|
||||
|
||||
public synchronized Class<?> loadRobotMainClass(boolean resolve) throws ClassNotFoundException {
|
||||
try {
|
||||
if (robotClass == null) {
|
||||
robotClass = loadClass(fullClassName, resolve);
|
||||
|
||||
if (!IBasicRobot.class.isAssignableFrom(robotClass)) {
|
||||
// that's not robot
|
||||
return null;
|
||||
}
|
||||
if (resolve) {
|
||||
// resolve methods to see more referenced classes
|
||||
robotClass.getMethods();
|
||||
|
||||
// iterate thru dependencies until we didn't found any new
|
||||
HashSet<String> clone;
|
||||
|
||||
do {
|
||||
clone = new HashSet<String>(referencedClasses);
|
||||
for (String reference : clone) {
|
||||
testPackages(reference);
|
||||
if (!isSystemClass(reference)) {
|
||||
loadClass(reference, true);
|
||||
}
|
||||
}
|
||||
} while (referencedClasses.size() != clone.size());
|
||||
}
|
||||
} else {
|
||||
warnIfStaticRobotInstanceFields();
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
robotClass = null;
|
||||
throw new ClassNotFoundException(e.getMessage(), e);
|
||||
}
|
||||
return robotClass;
|
||||
}
|
||||
|
||||
public IBasicRobot createRobotInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
||||
loadRobotMainClass(true);
|
||||
return (IBasicRobot) robotClass.newInstance();
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
// Bug fix [2930266] - Robot static data isn't being GCed after battle
|
||||
for (String className : getReferencedClasses()) {
|
||||
cleanStaticReferences(className);
|
||||
}
|
||||
|
||||
parent = null;
|
||||
codeSource = null;
|
||||
robotProxy = null;
|
||||
robotClass = null;
|
||||
referencedClasses = null;
|
||||
foundSystemClasses = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans all static field references on a class.
|
||||
*
|
||||
* @param className the name of the class containing the static references to clean.
|
||||
*/
|
||||
private void cleanStaticReferences(String className) {
|
||||
if (isSystemClass(className)) {
|
||||
return;
|
||||
}
|
||||
Class<?> type = null;
|
||||
try {
|
||||
type = loadRobotClassLocaly(className, false);
|
||||
if (type != null) {
|
||||
for (Field field : getAllFields(new ArrayList<Field>(), type)) {
|
||||
if (isStaticReference(field)) {
|
||||
cleanStaticReference(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException t) {
|
||||
Logger.logError(t);
|
||||
}
|
||||
}
|
||||
|
||||
private void warnIfStaticRobotInstanceFields() {
|
||||
if (staticRobotInstanceWarning == null) {
|
||||
List<Field> staticRobotReferences = new ArrayList<Field>();
|
||||
|
||||
for (String className : getReferencedClasses()) { // Bug fix [3028102] - ConcurrentModificationException
|
||||
if (isSystemClass(className)) {
|
||||
continue;
|
||||
}
|
||||
Class<?> type = null;
|
||||
try {
|
||||
type = loadRobotClassLocaly(className, false);
|
||||
} catch (RuntimeException t) {
|
||||
continue;
|
||||
}
|
||||
if (type != null) {
|
||||
for (Field field : getAllFields(new ArrayList<Field>(), type)) {
|
||||
if (isStaticReference(field) && IBasicRobot.class.isAssignableFrom(field.getType())
|
||||
&& field.getAnnotation(robocode.annotation.SafeStatic.class) == null) {
|
||||
staticRobotReferences.add(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (staticRobotReferences.size() > 0) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
buf.append("Warning: ").append(fullClassName).append(
|
||||
" uses static reference to a robot with the following field(s):");
|
||||
|
||||
for (Field field : staticRobotReferences) {
|
||||
buf.append("\n\t").append(field.getDeclaringClass().getName()).append('.').append(field.getName()).append(", which points to a ").append(
|
||||
field.getType().getName());
|
||||
}
|
||||
|
||||
staticRobotInstanceWarning = new String[] {
|
||||
buf.toString(),
|
||||
"Static references to robots can cause unwanted behaviour with the robot using these.",
|
||||
"Please change static robot references to non-static references and recompile the robot."};
|
||||
} else {
|
||||
staticRobotInstanceWarning = new String[] {}; // Signal that there is no warnings to cache
|
||||
}
|
||||
} else if (staticRobotInstanceWarning.length == 0) {
|
||||
return; // Return, as no warnings should be written out in the robot console
|
||||
}
|
||||
|
||||
// Write out warnings to the robot console
|
||||
if (robotProxy != null) {
|
||||
for (String line : staticRobotInstanceWarning) {
|
||||
robotProxy.getOut().println("SYSTEM: " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans a static field reference class, even when it is 'private static final'
|
||||
*
|
||||
* @param field the field to clean, if it is a static reference.
|
||||
*/
|
||||
private void cleanStaticReference(Field field) {
|
||||
if (field.getName().startsWith("const__")) {
|
||||
// Ignore 'const__' fields used with e.g. the Clojure language
|
||||
return;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
// In order to set a 'private static field', we need to fix the modifier, i.e. use magic! ;-)
|
||||
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
int modifiers = modifiersField.getInt(field);
|
||||
modifiersField.setInt(field, modifiers & ~Modifier.FINAL); // Remove the FINAL modifier
|
||||
field.set(null, null);
|
||||
|
||||
} catch (RuntimeException ignore) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all fields of a class (public, protected, private) and the ones inherited from all super classes.
|
||||
* @param fields the list where the fields will be added as a result of calling this method.
|
||||
* @param type the class to retrieve all the fields from
|
||||
* @return the list specified as input parameter containing all the retrieved fields
|
||||
*/
|
||||
private List<Field> getAllFields(List<Field> fields, Class<?> type) {
|
||||
if (type == null || isSystemClass(type.getName())) {
|
||||
return fields;
|
||||
}
|
||||
try {
|
||||
for (Field field: type.getDeclaredFields()) {
|
||||
fields.add(field);
|
||||
}
|
||||
} catch (RuntimeException ignore) {// NoClassDefFoundError does occur with some robots, e.g. sgp.Drunken [1.12]
|
||||
// We ignore all exceptions and errors here so we can proceed to retrieve
|
||||
// field from super classes.
|
||||
}
|
||||
if (type.getSuperclass() != null) {
|
||||
fields = getAllFields(fields, type.getSuperclass());
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specified class name is a Java system class or internal Robocode class.
|
||||
* @param className the class name to check.
|
||||
* @return true if the class name is a system class; false otherwise.
|
||||
*/
|
||||
private boolean isSystemClass(String className) {
|
||||
boolean isSystemClass = foundSystemClasses.contains(className);
|
||||
if (!isSystemClass) {
|
||||
try {
|
||||
if (findSystemClass(className) != null) {
|
||||
foundSystemClasses.add(className);
|
||||
isSystemClass = true;
|
||||
}
|
||||
} catch (ClassNotFoundException ignore) {}
|
||||
}
|
||||
return isSystemClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specified field is a static reference.
|
||||
*
|
||||
* @param field the field to check.
|
||||
* @return true if the field is static reference; false otherwise.
|
||||
*/
|
||||
private static boolean isStaticReference(Field field) {
|
||||
return Modifier.isStatic(field.getModifiers())
|
||||
&& !(field.getType().isPrimitive() || field.isEnumConstant() || field.isSynthetic());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,705 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.installer;
|
||||
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
|
||||
/**
|
||||
* Installer for Robocode.
|
||||
*
|
||||
* @author Mathew A. Nelsen (original)
|
||||
* @author Flemming N. Larsen (contributor)
|
||||
*/
|
||||
public class AutoExtract implements ActionListener {
|
||||
private JDialog licenseDialog;
|
||||
private boolean accepted;
|
||||
private static final String[] SPINNER = { "-", "\\", "|", "/"};
|
||||
private String message = "";
|
||||
private static File installDir;
|
||||
private static final String osName = System.getProperty("os.name");
|
||||
private static final double osVersion = doubleValue(System.getProperty("os.version"));
|
||||
private static final String javaVersion = System.getProperty("java.version");
|
||||
|
||||
private static double doubleValue(String s) {
|
||||
s = s.replaceAll("[^.0-9]", ""); // Remove invalid characters, e.g. "3.0-ARCH" become "3.0"
|
||||
|
||||
int p = s.indexOf(".");
|
||||
|
||||
if (p >= 0) {
|
||||
p = s.indexOf(".", p + 1);
|
||||
}
|
||||
if (p >= 0) {
|
||||
s = s.substring(0, p);
|
||||
}
|
||||
|
||||
double d = 0.0;
|
||||
|
||||
try {
|
||||
d = Double.parseDouble(s);
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
private boolean acceptLicense() {
|
||||
StringBuffer licenseText = new StringBuffer();
|
||||
|
||||
InputStream is;
|
||||
|
||||
try {
|
||||
JarFile extractJar = new JarFile("extract.jar");
|
||||
|
||||
is = extractJar.getInputStream(extractJar.getJarEntry("license/cpl-v10.html"));
|
||||
} catch (IOException e) {
|
||||
return true;
|
||||
}
|
||||
if (is == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
InputStreamReader isr = null;
|
||||
BufferedReader br = null;
|
||||
|
||||
try {
|
||||
isr = new InputStreamReader(is);
|
||||
br = new BufferedReader(isr);
|
||||
|
||||
String line = br.readLine();
|
||||
|
||||
while (line != null) {
|
||||
licenseText.append(line);
|
||||
line = br.readLine();
|
||||
}
|
||||
return acceptReject(licenseText.toString());
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println("Could not read line from license file: " + e);
|
||||
} finally {
|
||||
if (br != null) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (isr != null) {
|
||||
try {
|
||||
isr.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean acceptReject(String text) {
|
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
||||
licenseDialog = new JDialog();
|
||||
licenseDialog.setTitle("License Agreement");
|
||||
licenseDialog.setModal(true);
|
||||
licenseDialog.setLocation((screenSize.width - 500) / 2, (screenSize.height - 400) / 2);
|
||||
licenseDialog.setSize(500, 400);
|
||||
JTextPane t = new JTextPane();
|
||||
|
||||
t.setContentType("text/html");
|
||||
t.setText(text);
|
||||
t.setFont(new Font("Dialog", Font.PLAIN, 12));
|
||||
t.setEditable(false);
|
||||
|
||||
JScrollPane s = new JScrollPane();
|
||||
|
||||
s.setViewportView(t);
|
||||
|
||||
licenseDialog.getContentPane().setLayout(new BorderLayout());
|
||||
licenseDialog.getContentPane().add(s, BorderLayout.CENTER);
|
||||
|
||||
JPanel p = new JPanel();
|
||||
|
||||
p.setLayout(new BorderLayout());
|
||||
JButton b1 = new JButton("Accept");
|
||||
JButton b2 = new JButton("Cancel");
|
||||
|
||||
p.add(b1, BorderLayout.WEST);
|
||||
p.add(b2, BorderLayout.EAST);
|
||||
|
||||
b1.addActionListener(this);
|
||||
b2.addActionListener(this);
|
||||
|
||||
licenseDialog.getContentPane().add(p, BorderLayout.SOUTH);
|
||||
|
||||
licenseDialog.setVisible(true);
|
||||
|
||||
return accepted;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
accepted = e.getActionCommand().equals("Accept");
|
||||
licenseDialog.dispose();
|
||||
licenseDialog = null;
|
||||
}
|
||||
|
||||
private boolean extract(File installDir) {
|
||||
JDialog statusDialog = new JDialog();
|
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
||||
int width = 500;
|
||||
int height = 100;
|
||||
|
||||
statusDialog.setTitle("Installing");
|
||||
statusDialog.setLocation((screenSize.width - width) / 2, (screenSize.height - height) / 2);
|
||||
statusDialog.setSize(width, height);
|
||||
JLabel status = new JLabel();
|
||||
|
||||
statusDialog.getContentPane().setLayout(new BorderLayout());
|
||||
statusDialog.getContentPane().add(status, BorderLayout.CENTER);
|
||||
|
||||
statusDialog.setVisible(true);
|
||||
|
||||
FileOutputStream fos;
|
||||
String entryName;
|
||||
|
||||
byte buf[] = new byte[2048];
|
||||
|
||||
final String name = AutoExtract.class.getName().replaceAll("\\.", "/") + ".class";
|
||||
String urlJar = AutoExtract.class.getClassLoader().getResource(name).toString();
|
||||
final String src = urlJar.substring("jar:file:/".length(), urlJar.indexOf("!/"));
|
||||
|
||||
if (src.indexOf('!') > -1) {
|
||||
message = src + "\nContains an exclamation point. Please move the file to a different directory.";
|
||||
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
JarInputStream jarIS = null;
|
||||
|
||||
try {
|
||||
final URL url = new URL("file:/" + src);
|
||||
InputStream is = url.openStream();
|
||||
|
||||
jarIS = new JarInputStream(is);
|
||||
|
||||
JarEntry entry;
|
||||
|
||||
while ((entry = jarIS.getNextJarEntry()) != null) {
|
||||
int spin = 0;
|
||||
|
||||
entryName = entry.getName();
|
||||
if (entry.isDirectory()) {
|
||||
if (!entryName.startsWith("net") && !(entryName.equals("desktop") && isFreeBSD())) {
|
||||
File dir = new File(installDir, entry.getName());
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
System.err.println("Can't create dir: " + dir);
|
||||
}
|
||||
}
|
||||
} else if (!entryName.equals(name)) {
|
||||
|
||||
// Skip .bat, .sh, and .command files depending on which OS Robocode is installed
|
||||
|
||||
final String entryNameLC = entryName.toLowerCase();
|
||||
|
||||
boolean skipEntry = false;
|
||||
|
||||
final boolean isBatFile = entryNameLC.length() > ".bat".length() && entryNameLC.endsWith(".bat");
|
||||
final boolean isShFile = entryNameLC.length() > ".sh".length() && entryNameLC.endsWith(".sh");
|
||||
final boolean isCommandFile = entryNameLC.length() > ".command".length()
|
||||
&& entryNameLC.endsWith(".command");
|
||||
final boolean isDesktopFile = entryNameLC.startsWith("desktop/");
|
||||
|
||||
// Unix systems and Mac OS X
|
||||
if (File.separatorChar == '/') {
|
||||
// Skip .bat files under Unix and Mac OS X
|
||||
// Skip .command files under Unix
|
||||
skipEntry = isBatFile || (isCommandFile && !isMacOSX()) || (isDesktopFile && !isFreeBSD());
|
||||
} else {
|
||||
// Under Windows the .sh and .command files are skipped
|
||||
skipEntry = isShFile || isCommandFile || isDesktopFile;
|
||||
}
|
||||
|
||||
// If we are not skipping the entry, then copy from our .jar into the installation dir
|
||||
if (!skipEntry) {
|
||||
status.setText(entryName + " " + SPINNER[spin++]);
|
||||
|
||||
File out = new File(installDir, entryName);
|
||||
|
||||
File parentDirectory = new File(out.getParent());
|
||||
if (!parentDirectory.exists() && !parentDirectory.mkdirs()) {
|
||||
System.err.println("Can't create dir: " + parentDirectory);
|
||||
}
|
||||
fos = new FileOutputStream(out);
|
||||
|
||||
int index = 0;
|
||||
int num;
|
||||
int count = 0;
|
||||
|
||||
while ((num = jarIS.read(buf, 0, 2048)) != -1) {
|
||||
fos.write(buf, 0, num);
|
||||
index += num;
|
||||
count++;
|
||||
if (count > 80) {
|
||||
status.setText(entryName + " " + SPINNER[spin++] + " (" + index + " bytes)");
|
||||
if (spin > 3) {
|
||||
spin = 0;
|
||||
}
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
fos.close();
|
||||
|
||||
// Set file permissions for .sh and .command files under Unix and Mac OS X
|
||||
if (File.separatorChar == '/') {
|
||||
if (isShFile || isCommandFile) {
|
||||
// Grant read and execute access for everyone and also write access for the owner of the file
|
||||
Runtime.getRuntime().exec("chmod 755 " + out.toString());
|
||||
}
|
||||
}
|
||||
|
||||
status.setText(entryName + " " + SPINNER[spin] + " (" + index + " bytes)");
|
||||
}
|
||||
}
|
||||
}
|
||||
statusDialog.dispose();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
message = "Installation failed: " + e;
|
||||
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return false;
|
||||
} finally {
|
||||
if (jarIS != null) {
|
||||
try {
|
||||
jarIS.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String argv[]) {
|
||||
String suggestedDirName;
|
||||
|
||||
if (argv.length == 1) {
|
||||
suggestedDirName = argv[0];
|
||||
} else if (isWindowsOS()) {
|
||||
suggestedDirName = "C:\\robocode\\";
|
||||
} else {
|
||||
suggestedDirName = System.getProperty("user.home") + File.separator + "robocode" + File.separator;
|
||||
}
|
||||
|
||||
String message;
|
||||
|
||||
if (install(new File(suggestedDirName))) {
|
||||
message = "Installation successful";
|
||||
} else {
|
||||
message = "Installation cancelled";
|
||||
}
|
||||
|
||||
// Delete the class file with the installer and it's parent folders in the robocode home dir
|
||||
if (installDir != null) {
|
||||
String installerPath = AutoExtract.class.getName().replaceAll("\\.", "/") + "$1.class";
|
||||
|
||||
deleteFileAndParentDirsIfEmpty(new File(installDir, installerPath));
|
||||
}
|
||||
|
||||
JOptionPane.showMessageDialog(null, message);
|
||||
}
|
||||
|
||||
private static boolean install(File suggestedDir) {
|
||||
// Verify that the Java version is version 6 (1.6.0) or newer
|
||||
if (javaVersion.startsWith("1.") && javaVersion.charAt(2) < '5') {
|
||||
final String message = "Robocode requires Java 6 (1.6.0) or newer.\n"
|
||||
+ "Your system is currently running Java " + javaVersion + ".\n"
|
||||
+ "If you have not installed (or activated) at least\n" + "JRE 6 or JDK 6, please do so.";
|
||||
|
||||
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
|
||||
System.err.println(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set native look and feel
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (RuntimeException t) {// For some reason Ubuntu 7 can cause a NullPointerException when trying to getting the LAF
|
||||
}
|
||||
|
||||
AutoExtract extractor = new AutoExtract();
|
||||
|
||||
if (extractor.acceptLicense()) {
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int rc = JOptionPane.showConfirmDialog(null,
|
||||
"Robocode will be installed in:\n" + suggestedDir + "\nIs this ok?", "Installing Robocode",
|
||||
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
if (rc == JOptionPane.YES_OPTION) {
|
||||
installDir = suggestedDir;
|
||||
done = true;
|
||||
} else if (rc == JOptionPane.NO_OPTION) {
|
||||
Object r = JOptionPane.showInputDialog(null, "Please type in the installation directory",
|
||||
"Installation Directory", JOptionPane.PLAIN_MESSAGE, null, null, suggestedDir);
|
||||
|
||||
if (r != null) {
|
||||
suggestedDir = new File(((String) r).trim());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (rc == JOptionPane.CANCEL_OPTION) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!installDir.exists() && !installDir.mkdirs()) {
|
||||
System.err.println("Can't create dir: " + installDir);
|
||||
return false;
|
||||
}
|
||||
deleteOldLibs(installDir);
|
||||
|
||||
// The .robotcache has been renamed to .data
|
||||
File robotsCacheDir = new File(installDir, "robots/.robotcache");
|
||||
File robotsDataDir = new File(installDir, "robots/.data");
|
||||
|
||||
if (robotsCacheDir.exists()) {
|
||||
// Rename ".robotcache" into ".data"
|
||||
robotsCacheDir.renameTo(robotsDataDir);
|
||||
}
|
||||
|
||||
// Fix problem with .data starting with a underscore dir by
|
||||
// renaming files containing ".data/_" into ".data"
|
||||
if (robotsDataDir.exists()) {
|
||||
File underScoreDir = new File(robotsDataDir, "_");
|
||||
String[] list = underScoreDir.list();
|
||||
|
||||
if (list != null) {
|
||||
for (String fileName : list) {
|
||||
File file = new File(underScoreDir, fileName);
|
||||
|
||||
file.renameTo(new File(robotsDataDir, fileName));
|
||||
}
|
||||
underScoreDir.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Create shortcuts and file associations
|
||||
if (extractor.extract(installDir)) {
|
||||
extractor.createShortcuts(installDir, "robocode.bat", "Robocode", "Robocode");
|
||||
extractor.createFileAssociations(installDir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void deleteOldLibs(File installDir) {
|
||||
File libs = new File(installDir, "libs");
|
||||
|
||||
if (libs.exists()) {
|
||||
final File[] del = libs.listFiles(new FilenameFilter() {
|
||||
|
||||
public boolean accept(File dir, String name) {
|
||||
String test = name.toLowerCase();
|
||||
|
||||
return test.endsWith(".jar") || test.endsWith(".dll");
|
||||
}
|
||||
});
|
||||
|
||||
for (File d : del) {
|
||||
if (!d.delete()) {
|
||||
System.err.println("Can't delete: " + d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
for (File file : dir.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
// Skip directories ending with ".data"
|
||||
if (file.getName().endsWith(".data")) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// Test for symlink and ignore.
|
||||
// Robocode won't create one, but just in case a user does...
|
||||
if (file.getCanonicalFile().getParentFile().equals(dir.getCanonicalFile())) {
|
||||
deleteDir(file);
|
||||
if (file.exists() && !file.delete()) {
|
||||
System.err.println("Can't delete: " + file);
|
||||
}
|
||||
} else {
|
||||
System.out.println("Warning: " + file + " may be a symlink. It has been ignored");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println(
|
||||
"Warning: Cannot determine canonical file for " + file + ". It has been ignored");
|
||||
}
|
||||
} else {
|
||||
if (file.exists() && !file.delete()) {
|
||||
System.err.println("Can't delete: " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dir.delete();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void createShortcuts(File installDir, String runnable, String folder, String name) {
|
||||
if (osName.toLowerCase().indexOf("win") == 0) {
|
||||
if (createWindowsShortcuts(installDir, runnable, folder, name) == false) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
message + "\n" + "To start Robocode, enter the following at a command prompt:\n" + "cd "
|
||||
+ installDir.getAbsolutePath() + "\n" + "robocode.bat");
|
||||
}
|
||||
} else if (osName.toLowerCase().indexOf("mac") == 0) {
|
||||
if (osVersion >= 10.1) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
message + "\n" + "To start Robocode, browse to " + installDir + " then double-click robocode.sh\n");
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
message + "\n" + "To start Robocode, enter the following at a command prompt:\n"
|
||||
+ installDir.getAbsolutePath() + "/robocode.sh");
|
||||
}
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
message + "\n" + "To start Robocode, enter the following at a command prompt:\n"
|
||||
+ installDir.getAbsolutePath() + "/robocode.sh");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean createWindowsShortcuts(File installDir, String runnable, String folder, String name) {
|
||||
int rc = JOptionPane.showConfirmDialog(null,
|
||||
"Would you like to install a shortcut to Robocode in the Start menu? (Recommended)", "Create Shortcuts",
|
||||
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
if (rc != JOptionPane.YES_OPTION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String command = getWindowsCmd() + " cscript.exe ";
|
||||
|
||||
try {
|
||||
File shortcutMaker = new File(installDir, "makeshortcut.vbs");
|
||||
PrintStream out = new PrintStream(new FileOutputStream(shortcutMaker));
|
||||
|
||||
out.println("WScript.Echo(\"Creating shortcuts...\")");
|
||||
out.println("Set Shell = CreateObject (\"WScript.Shell\")");
|
||||
out.println("Set fso = CreateObject(\"Scripting.FileSystemObject\")");
|
||||
out.println("ProgramsPath = Shell.SpecialFolders(\"Programs\")");
|
||||
out.println("if (not(fso.folderExists(ProgramsPath + \"\\\\" + folder + "\"))) Then");
|
||||
out.println(" fso.CreateFolder(ProgramsPath + \"\\\\" + folder + "\")");
|
||||
out.println("End If");
|
||||
out.println("Set link = Shell.CreateShortcut(ProgramsPath + \"\\\\" + folder + "\\\\" + name + ".lnk\")");
|
||||
out.println("link.Arguments = \"\"");
|
||||
out.println("link.Description = \"" + name + "\"");
|
||||
out.println("link.HotKey = \"\"");
|
||||
out.println("link.IconLocation = \"" + escaped(installDir.getAbsolutePath()) + "\\\\" + "robocode.ico,0\"");
|
||||
out.println("link.TargetPath = \"" + escaped(installDir.getAbsolutePath()) + "\\\\" + runnable + "\"");
|
||||
out.println("link.WindowStyle = 1");
|
||||
out.println("link.WorkingDirectory = \"" + escaped(installDir.getAbsolutePath()) + "\"");
|
||||
out.println("link.Save()");
|
||||
out.println("DesktopPath = Shell.SpecialFolders(\"Desktop\")");
|
||||
out.println("Set link = Shell.CreateShortcut(DesktopPath + \"\\\\" + name + ".lnk\")");
|
||||
out.println("link.Arguments = \"\"");
|
||||
out.println("link.Description = \"" + name + "\"");
|
||||
out.println("link.HotKey = \"\"");
|
||||
out.println("link.IconLocation = \"" + escaped(installDir.getAbsolutePath()) + "\\\\" + "robocode.ico,0\"");
|
||||
out.println("link.TargetPath = \"" + escaped(installDir.getAbsolutePath()) + "\\\\" + runnable + "\"");
|
||||
out.println("link.WindowStyle = 1");
|
||||
out.println("link.WorkingDirectory = \"" + escaped(installDir.getAbsolutePath()) + "\"");
|
||||
out.println("link.Save()");
|
||||
out.println("WScript.Echo(\"Shortcuts created.\")");
|
||||
|
||||
out.close();
|
||||
|
||||
Process p = Runtime.getRuntime().exec(command + " makeshortcut.vbs", null, installDir);
|
||||
int rv = p.waitFor();
|
||||
|
||||
if (rv != 0) {
|
||||
System.err.println("Can't create shortcut: " + shortcutMaker);
|
||||
return false;
|
||||
}
|
||||
JOptionPane.showMessageDialog(null,
|
||||
message + "\n" + "A Robocode program group has been added to your Start menu\n"
|
||||
+ "A Robocode icon has been added to your desktop.");
|
||||
if (!shortcutMaker.delete()) {
|
||||
System.err.println("Can't delete: " + shortcutMaker);
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void createFileAssociations(File installDir) {
|
||||
if (isWindowsOS()) {
|
||||
createWindowsFileAssociations(installDir);
|
||||
}
|
||||
}
|
||||
|
||||
private void createWindowsFileAssociations(File installDir) {
|
||||
int rc = JOptionPane.showConfirmDialog(null,
|
||||
"Would you like to create file associations for Robocode in\n"
|
||||
+ "the Windows Registry for the file extensions .battle and .br?\n"
|
||||
+ "Please notice that you might need to grant permission to add\n"
|
||||
+ "the file associations in the Registry, and you must be an\n"
|
||||
+ "administrator or granted permission to change the registry.",
|
||||
"Create File Associations",
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
if (rc != JOptionPane.YES_OPTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
File file = null;
|
||||
PrintStream out = null;
|
||||
|
||||
try {
|
||||
file = new File(installDir + "\\FileAssoc.reg");
|
||||
|
||||
out = new PrintStream(new FileOutputStream(file));
|
||||
|
||||
String installPath = installDir.getAbsolutePath();
|
||||
|
||||
out.print(
|
||||
createWindowsRegistryFileAssociation(installPath, ".battle", "Robocode.BattleSpecification",
|
||||
"Robocode Battle Specification", "-battle"));
|
||||
out.print(
|
||||
createWindowsRegistryFileAssociation(installPath, ".br", "Robocode.BattleRecord", "Robocode Battle Record",
|
||||
"-replay"));
|
||||
|
||||
out.close();
|
||||
out = null;
|
||||
|
||||
Process p = Runtime.getRuntime().exec(getWindowsCmd() + file.getAbsolutePath(), null, null);
|
||||
int rv;
|
||||
|
||||
try {
|
||||
rv = p.waitFor();
|
||||
if (rv != 0) {
|
||||
System.err.println("Could not create association(s)");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (file != null) {
|
||||
if (!file.delete()) {
|
||||
System.err.println("Could not delete the file: " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String createWindowsRegistryFileAssociation(String installDir, String fileExt, String progId, String description, String robocodeCmdParam) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
final String HKCR = "[HKEY_CLASSES_ROOT\\";
|
||||
|
||||
sb.append("REGEDIT4\n");
|
||||
sb.append(HKCR).append(fileExt).append("]\n");
|
||||
sb.append("@=\"").append(progId).append("\"\n");
|
||||
sb.append(HKCR).append(progId).append("]\n");
|
||||
sb.append("@=\"").append(description).append("\"\n");
|
||||
sb.append(HKCR).append(progId).append("\\shell]\n");
|
||||
sb.append(HKCR).append(progId).append("\\shell\\open]\n");
|
||||
sb.append(HKCR).append(progId).append("\\shell\\open\\command]\n");
|
||||
sb.append("@=\"").append(getWindowsCmd()).append("\\\"cd \\\"").append(installDir.replaceAll("[\\\\]", "\\\\\\\\")).append("\\\" && robocode.bat ").append(robocodeCmdParam).append(
|
||||
" \\\"%1\\\"\\\"\"\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String escaped(String s) {
|
||||
StringBuffer eascaped = new StringBuffer();
|
||||
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) == '\\') {
|
||||
eascaped.append('\\');
|
||||
}
|
||||
eascaped.append(s.charAt(i));
|
||||
}
|
||||
return eascaped.toString();
|
||||
}
|
||||
|
||||
private static boolean isWindowsOS() {
|
||||
return osName.startsWith("Windows ");
|
||||
}
|
||||
|
||||
private static boolean isMacOSX() {
|
||||
return osName.startsWith("Mac OS X");
|
||||
}
|
||||
|
||||
private static boolean isFreeBSD() {
|
||||
return osName.equals("FreeBSD");
|
||||
}
|
||||
|
||||
private static String getWindowsCmd() {
|
||||
String os = System.getProperty("os.name");
|
||||
|
||||
return ((os.equals("Windows 95") || os.equals("Windows 95") || os.equals("Windows ME"))
|
||||
? "command.com"
|
||||
: "cmd.exe")
|
||||
+ " /C ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file and afterwards deletes the parent directories that are empty.
|
||||
*
|
||||
* @param file the file or directory to delete
|
||||
* @return true if success
|
||||
*/
|
||||
private static boolean deleteFileAndParentDirsIfEmpty(final File file) {
|
||||
boolean wasDeleted = false;
|
||||
|
||||
if (file != null && file.exists()) {
|
||||
if (file.isDirectory()) {
|
||||
wasDeleted = deleteDir(file);
|
||||
} else {
|
||||
wasDeleted = file.delete();
|
||||
|
||||
File parent = file;
|
||||
|
||||
while (wasDeleted && (parent = parent.getParentFile()) != null) {
|
||||
// Delete parent directory, but only if it is empty
|
||||
File[] files = parent.listFiles();
|
||||
|
||||
if (files != null && files.length == 0) {
|
||||
wasDeleted = deleteDir(parent);
|
||||
} else {
|
||||
wasDeleted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return wasDeleted;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,657 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.roborumble.netengine;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Properties;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import static net.sf.robocode.roborumble.util.PropertiesUtil.getProperties;
|
||||
|
||||
|
||||
/**
|
||||
* Utility class for downloading files from the net and copying files.
|
||||
*
|
||||
* @author Flemming N. Larsen (original)
|
||||
*/
|
||||
public class FileTransfer {
|
||||
|
||||
private final static int DEFAULT_CONNECTION_TIMEOUT = 10000; // 10 seconds
|
||||
private final static int DEFAULT_READ_TIMEOUT = 10000; // 10 seconds
|
||||
private final static int DEFAULT_SESSION_TIMEOUT = 10000; // 10 seconds
|
||||
|
||||
private static int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
|
||||
private static int readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
private static int sessionTimeout = DEFAULT_SESSION_TIMEOUT;
|
||||
|
||||
static {
|
||||
readProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the download status returned when downloading files.
|
||||
*/
|
||||
public enum DownloadStatus {
|
||||
OK, // The download was succesful
|
||||
COULD_NOT_CONNECT, // Connection problem
|
||||
FILE_NOT_FOUND, // The file to download was not found
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Daemon worker thread containing a 'finish' flag for waiting and notifying when the thread has finished it's job.
|
||||
*/
|
||||
private static class WorkerThread extends Thread {
|
||||
|
||||
final Object monitor = new Object();
|
||||
|
||||
volatile boolean isFinished;
|
||||
|
||||
public WorkerThread(String name) {
|
||||
super(name);
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
void notifyFinish() {
|
||||
// Notify that this thread is finish
|
||||
synchronized (monitor) {
|
||||
isFinished = true;
|
||||
monitor.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a session id for keeping a session on a HTTP site.
|
||||
*
|
||||
* @param url is the url of the HTTP site.
|
||||
*
|
||||
* @return a session id for keeping a session on a HTTP site or null if no session is available.
|
||||
*/
|
||||
public static String getSessionId(String url) {
|
||||
HttpURLConnection conn = null;
|
||||
|
||||
try {
|
||||
// Open connection
|
||||
conn = FileTransfer.connectToHttpInputConnection(new URL(url));
|
||||
if (conn == null) {
|
||||
throw new IOException("Could not open connection to '" + url + "'");
|
||||
}
|
||||
|
||||
// Get a session id if available
|
||||
final GetSessionIdThread sessionIdThread = new GetSessionIdThread(conn);
|
||||
|
||||
sessionIdThread.start();
|
||||
|
||||
// Wait for the session id
|
||||
synchronized (sessionIdThread.monitor) {
|
||||
while (!sessionIdThread.isFinished) {
|
||||
try {
|
||||
sessionIdThread.monitor.wait(sessionTimeout);
|
||||
sessionIdThread.interrupt();
|
||||
} catch (InterruptedException e) {
|
||||
// Immediately reasserts the exception by interrupting the caller thread itself
|
||||
Thread.currentThread().interrupt();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the session id
|
||||
return sessionIdThread.sessionId;
|
||||
|
||||
} catch (final IOException e) {
|
||||
return null;
|
||||
} finally {
|
||||
// Make sure the connection is disconnected.
|
||||
// This will cause threads using the connection to throw an exception
|
||||
// and thereby terminate if they were hanging.
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Worker thread used for getting the session id of an already open HTTP connection.
|
||||
*/
|
||||
private final static class GetSessionIdThread extends WorkerThread {
|
||||
|
||||
// The resulting session id to read out
|
||||
String sessionId;
|
||||
|
||||
final HttpURLConnection conn;
|
||||
|
||||
GetSessionIdThread(HttpURLConnection conn) {
|
||||
super("FileTransfer: Get session ID");
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Get the cookie value
|
||||
final String cookieVal = conn.getHeaderField("Set-Cookie");
|
||||
|
||||
// Extract the session id from the cookie value
|
||||
if (cookieVal != null) {
|
||||
sessionId = cookieVal.substring(0, cookieVal.indexOf(";"));
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
sessionId = null;
|
||||
}
|
||||
// Notify that this thread is finish
|
||||
notifyFinish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a file from a HTTP site.
|
||||
*
|
||||
* @param url is the url of the HTTP site to download the file from.
|
||||
* @param filename is the filename of the destination file.
|
||||
* @param sessionId is an optional session id if the download is session based.
|
||||
* @return the download status, which is DownloadStatus.OK if the download completed successfully; otherwise an
|
||||
* error occurred.
|
||||
*/
|
||||
public static DownloadStatus download(String url, String filename, String sessionId) {
|
||||
HttpURLConnection conn = null;
|
||||
|
||||
try {
|
||||
// Create connection
|
||||
conn = connectToHttpInputConnection(new URL(url), sessionId);
|
||||
if (conn == null) {
|
||||
throw new IOException("Could not open connection to: " + url);
|
||||
}
|
||||
|
||||
// Begin the download
|
||||
final DownloadThread downloadThread = new DownloadThread(conn, filename);
|
||||
|
||||
downloadThread.start();
|
||||
|
||||
// Wait for the download to complete
|
||||
synchronized (downloadThread.monitor) {
|
||||
while (!downloadThread.isFinished) {
|
||||
try {
|
||||
downloadThread.monitor.wait();
|
||||
} catch (InterruptedException e) {
|
||||
return DownloadStatus.COULD_NOT_CONNECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the download status
|
||||
return downloadThread.status;
|
||||
|
||||
} catch (final IOException e) {
|
||||
return DownloadStatus.COULD_NOT_CONNECT;
|
||||
} finally {
|
||||
// Make sure the connection is disconnected.
|
||||
// This will cause threads using the connection to throw an exception
|
||||
// and thereby terminate if they were hanging.
|
||||
try {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
} catch (RuntimeException ignore) {// we expect this, right ?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Worker thread used for downloading a file from an already open HTTP connection.
|
||||
*/
|
||||
private final static class DownloadThread extends WorkerThread {
|
||||
|
||||
// The download status to be read out
|
||||
DownloadStatus status = DownloadStatus.COULD_NOT_CONNECT; // Default error
|
||||
|
||||
final HttpURLConnection conn;
|
||||
final String filename;
|
||||
|
||||
InputStream in;
|
||||
OutputStream out;
|
||||
|
||||
DownloadThread(HttpURLConnection conn, String filename) {
|
||||
super("FileTransfer: Download");
|
||||
this.conn = conn;
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Start getting the response code
|
||||
final GetResponseCodeThread responseThread = new GetResponseCodeThread(conn);
|
||||
|
||||
responseThread.start();
|
||||
|
||||
// Wait for the response to finish
|
||||
synchronized (responseThread.monitor) {
|
||||
while (!responseThread.isFinished) {
|
||||
try {
|
||||
responseThread.monitor.wait(sessionTimeout);
|
||||
responseThread.interrupt();
|
||||
} catch (InterruptedException e) {
|
||||
notifyFinish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final int responseCode = responseThread.responseCode;
|
||||
|
||||
if (responseCode == -1) {
|
||||
// Terminate if we did not get the response code
|
||||
notifyFinish();
|
||||
return;
|
||||
|
||||
} else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
// Terminate if the HTTP page containing the file was not found
|
||||
status = DownloadStatus.FILE_NOT_FOUND;
|
||||
notifyFinish();
|
||||
return;
|
||||
|
||||
} else if (responseCode != HttpURLConnection.HTTP_OK) {
|
||||
// Generally, terminate if did not receive a OK response
|
||||
notifyFinish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Start getting the size of the file to download
|
||||
final GetContentLengthThread contentLengthThread = new GetContentLengthThread(conn);
|
||||
|
||||
contentLengthThread.start();
|
||||
|
||||
// Wait for the file size
|
||||
synchronized (contentLengthThread.monitor) {
|
||||
while (!contentLengthThread.isFinished) {
|
||||
try {
|
||||
contentLengthThread.monitor.wait(sessionTimeout);
|
||||
contentLengthThread.interrupt();
|
||||
} catch (InterruptedException e) {
|
||||
notifyFinish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final int size = contentLengthThread.contentLength;
|
||||
|
||||
if (size == -1) {
|
||||
// Terminate if we did not get the content length
|
||||
notifyFinish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the input stream from the connection
|
||||
in = getInputStream(conn);
|
||||
|
||||
// Prepare the output stream for the file output
|
||||
out = new FileOutputStream(filename);
|
||||
|
||||
// Download the file
|
||||
|
||||
final byte[] buf = new byte[4096];
|
||||
|
||||
int totalRead = 0;
|
||||
int bytesRead;
|
||||
|
||||
// Start thread for reading bytes into the buffer
|
||||
|
||||
while (totalRead < size) {
|
||||
// Start reading bytes into the buffer
|
||||
final ReadInputStreamToBufferThread readThread = new ReadInputStreamToBufferThread(in, buf);
|
||||
|
||||
readThread.start();
|
||||
|
||||
// Wait for the reading to finish
|
||||
synchronized (readThread.monitor) {
|
||||
while (!readThread.isFinished) {
|
||||
try {
|
||||
readThread.monitor.wait(sessionTimeout);
|
||||
readThread.interrupt();
|
||||
} catch (InterruptedException e) {
|
||||
notifyFinish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bytesRead = readThread.bytesRead;
|
||||
if (bytesRead == -1) {
|
||||
// Read completed has completed
|
||||
notifyFinish();
|
||||
break;
|
||||
}
|
||||
|
||||
// Write the byte buffer to the output
|
||||
out.write(buf, 0, bytesRead);
|
||||
|
||||
totalRead += bytesRead;
|
||||
}
|
||||
|
||||
// If we reached this point, the download was successful
|
||||
status = DownloadStatus.OK;
|
||||
|
||||
notifyFinish();
|
||||
|
||||
} catch (final IOException e) {
|
||||
status = DownloadStatus.COULD_NOT_CONNECT;
|
||||
} finally {
|
||||
// Make sure the input stream is closed
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// Make sure the output stream is closed
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Worker thread used for getting the response code of an already open HTTP connection.
|
||||
*/
|
||||
final static class GetResponseCodeThread extends WorkerThread {
|
||||
|
||||
// The response code to read out
|
||||
int responseCode;
|
||||
|
||||
final HttpURLConnection conn;
|
||||
|
||||
GetResponseCodeThread(HttpURLConnection conn) {
|
||||
super("FileTransfer: Get response code");
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Get the response code
|
||||
responseCode = conn.getResponseCode();
|
||||
} catch (final Exception e) {
|
||||
responseCode = -1;
|
||||
}
|
||||
// Notify that this thread is finish
|
||||
notifyFinish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Worker thread used for getting the content length of an already open HTTP connection.
|
||||
*/
|
||||
final static class GetContentLengthThread extends WorkerThread {
|
||||
|
||||
// The content length to read out
|
||||
int contentLength;
|
||||
|
||||
final HttpURLConnection conn;
|
||||
|
||||
GetContentLengthThread(HttpURLConnection conn) {
|
||||
super("FileTransfer: Get content length");
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Get the content length
|
||||
contentLength = conn.getContentLength();
|
||||
} catch (final Exception e) {
|
||||
contentLength = -1;
|
||||
}
|
||||
// Notify that this thread is finish
|
||||
notifyFinish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Worker thread used for reading bytes from an already open input stream into a byte buffer.
|
||||
*/
|
||||
final static class ReadInputStreamToBufferThread extends WorkerThread {
|
||||
|
||||
int bytesRead;
|
||||
|
||||
final InputStream in;
|
||||
|
||||
final byte[] buf;
|
||||
|
||||
ReadInputStreamToBufferThread(InputStream in, byte[] buf) {
|
||||
super("FileTransfer: Read input stream to buffer");
|
||||
this.in = in;
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Read bytes into the buffer
|
||||
bytesRead = in.read(buf);
|
||||
} catch (final Exception e) {
|
||||
bytesRead = -1;
|
||||
}
|
||||
// Notify that this thread is finish
|
||||
notifyFinish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file into another file.
|
||||
*
|
||||
* @param srcFile is the filename of the source file to copy.
|
||||
* @param destFile is the filename of the destination file to copy the file into.
|
||||
* @return true if the file was copied; false otherwise
|
||||
*/
|
||||
public static boolean copy(String srcFile, String destFile) {
|
||||
FileInputStream in = null;
|
||||
FileOutputStream out = null;
|
||||
|
||||
try {
|
||||
if (srcFile.equals(destFile)) {
|
||||
throw new IOException("You cannot copy a file onto itself");
|
||||
}
|
||||
final byte[] buf = new byte[4096];
|
||||
|
||||
in = new FileInputStream(srcFile);
|
||||
out = new FileOutputStream(destFile);
|
||||
|
||||
while (in.available() > 0) {
|
||||
out.write(buf, 0, in.read(buf, 0, buf.length));
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens and connects to a {@link java.net.HttpURLConnection} for input only, and where the connection timeout and
|
||||
* read timeout are controlled by properties.
|
||||
*
|
||||
* @param url is the URL to open a connection to.
|
||||
* @return a HttpURLConnection intended for reading input only.
|
||||
* @throws IOException if an I/O exception occurs.
|
||||
*/
|
||||
public static HttpURLConnection connectToHttpInputConnection(URL url) throws IOException {
|
||||
return connectToHttpInputConnection(url, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens and connects to a {@link java.net.HttpURLConnection} for input only, and where the connection timeout and
|
||||
* read timeout are controlled by properties.
|
||||
*
|
||||
* @param url is the URL to open a connection to.
|
||||
* @param sessionId is a optional session id.
|
||||
* @return a HttpURLConnection intended for reading input only.
|
||||
* @throws IOException if an I/O exception occurs.
|
||||
*/
|
||||
public static HttpURLConnection connectToHttpInputConnection(URL url, String sessionId) throws IOException {
|
||||
HttpURLConnection conn = (HttpURLConnection) openURLConnection(url, false); // not for output
|
||||
|
||||
conn.setRequestMethod("GET");
|
||||
if (sessionId != null) {
|
||||
conn.setRequestProperty("Cookie", sessionId);
|
||||
}
|
||||
conn.connect();
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a {link {@link java.net.URLConnection} for output (and input) where the connection timeout and read timeout
|
||||
* are controlled by properties.
|
||||
*
|
||||
* @param url is the URL to open.
|
||||
* @return a URLConnection for output.
|
||||
* @throws IOException if an I/O exception occurs.
|
||||
*/
|
||||
public static URLConnection openOutputURLConnection(URL url) throws IOException {
|
||||
return openURLConnection(url, true); // for output
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient method used for getting an input stream from an URLConnection.
|
||||
* This method checks if a GZIPInputStream or InflaterInputStream should be used to wrap the input stream from the
|
||||
* URLConnection depending on the content encoding.
|
||||
*
|
||||
* @param conn is the URLConnection
|
||||
* @return an input stream from the URLConnection, which can be a GZIPInputStream or InflaterInputStream.
|
||||
* @throws IOException if an I/O exception occurs.
|
||||
*/
|
||||
public static InputStream getInputStream(URLConnection conn) throws IOException {
|
||||
// Get input stream
|
||||
InputStream in = conn.getInputStream();
|
||||
|
||||
// Get the encoding returned by the server
|
||||
String encoding = conn.getContentEncoding();
|
||||
|
||||
// Check if we need to use a gzip or inflater input stream depending on the content encoding
|
||||
if ("gzip".equalsIgnoreCase(encoding)) {
|
||||
in = new GZIPInputStream(in);
|
||||
} else if ("deflate".equalsIgnoreCase(encoding)) {
|
||||
in = new InflaterInputStream(in);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient method used for getting an output stream from an URLConnection.
|
||||
* This method checks if a GZIPOutputStream or DeflaterOutputStream should be used to wrap the output stream from
|
||||
* the URLConnection depending on the content encoding.
|
||||
*
|
||||
* @param conn is the URLConnection
|
||||
* @return an output stream from the URLConnection, which can be a GZIPOutputStream or DeflaterOutputStream.
|
||||
* @throws IOException if an I/O exception occurs.
|
||||
*/
|
||||
public static OutputStream getOutputStream(URLConnection conn) throws IOException {
|
||||
// Get output stream
|
||||
OutputStream out = conn.getOutputStream();
|
||||
|
||||
// // Get the encoding returned by the server
|
||||
// String encoding = conn.getContentEncoding();
|
||||
//
|
||||
// // Check if we need to use a gzip or inflater input stream depending on the content encoding
|
||||
// if ("gzip".equalsIgnoreCase(encoding)) {
|
||||
// out = new GZIPOutputStream(out);
|
||||
// } else if ("deflate".equalsIgnoreCase(encoding)) {
|
||||
// out = new DeflaterOutputStream(out);
|
||||
// }
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a {link {@link java.net.URLConnection} for input and optional output where the connection timeout and read
|
||||
* timeout are controlled by properties.
|
||||
*
|
||||
* @param url is the URL to open.
|
||||
* @param isOutput is a flag specifying if the opened connection is for output.
|
||||
* @return a URLConnection.
|
||||
* @throws IOException if an I/O exception occurs.
|
||||
*/
|
||||
public static URLConnection openURLConnection(URL url, boolean isOutput) throws IOException {
|
||||
URLConnection conn = url.openConnection();
|
||||
|
||||
conn.setDoInput(true);
|
||||
conn.setDoOutput(isOutput);
|
||||
|
||||
conn.setConnectTimeout(connectionTimeout);
|
||||
conn.setReadTimeout(readTimeout);
|
||||
|
||||
if (!isOutput) {
|
||||
// Allow both GZip and Deflate (ZLib) encodings
|
||||
conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
|
||||
conn.setRequestProperty("User-Agent", "RoboRumble@Home - gzip, deflate");
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the roborumble.properties file and stores property values into global variables.
|
||||
*/
|
||||
private static void readProperties() {
|
||||
Properties props = getProperties("./roborumble/roborumble.properties");
|
||||
|
||||
// Get connection timeout
|
||||
String value = props.getProperty("connection.open.timeout");
|
||||
|
||||
if (value != null) {
|
||||
try {
|
||||
connectionTimeout = Integer.parseInt(value);
|
||||
} catch (NumberFormatException ignore) {}
|
||||
}
|
||||
|
||||
// Get connection read timeout
|
||||
value = props.getProperty("connection.read.timeout");
|
||||
if (value != null) {
|
||||
try {
|
||||
readTimeout = Integer.parseInt(value);
|
||||
} catch (NumberFormatException ignore) {}
|
||||
}
|
||||
|
||||
// Get download session timeout
|
||||
value = props.getProperty("download.session.timeout");
|
||||
if (value != null) {
|
||||
try {
|
||||
sessionTimeout = Integer.parseInt(value);
|
||||
} catch (NumberFormatException ignore) {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package tested.robots;
|
||||
|
||||
|
||||
import robocode.AdvancedRobot;
|
||||
import robocode.ScannedRobotEvent;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
public class AwtAttack extends AdvancedRobot {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// noinspection InfiniteLoopStatement
|
||||
for (;;) {
|
||||
turnLeft(100);
|
||||
ahead(10);
|
||||
turnLeft(100);
|
||||
back(10);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScannedRobot(ScannedRobotEvent event) {
|
||||
awtAttack();
|
||||
}
|
||||
|
||||
private void awtAttack() {
|
||||
try {
|
||||
Runnable doHack = new Runnable() {
|
||||
public void run() {
|
||||
writeAttack();
|
||||
|
||||
JFrame frame;
|
||||
|
||||
frame = new JFrame();
|
||||
frame.setName("Hack");
|
||||
frame.setVisible(true);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
javax.swing.SwingUtilities.invokeLater(doHack);
|
||||
} catch (RuntimeException e) {
|
||||
// swalow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAttack() {
|
||||
FileOutputStream fs;
|
||||
|
||||
try {
|
||||
fs = new FileOutputStream("C:\\Robocode.attack");
|
||||
fs.write(0xBA);
|
||||
fs.write(0xDF);
|
||||
fs.write(0x00);
|
||||
fs.write(0xD0);
|
||||
fs.close();
|
||||
out.println("Hacked!!!");
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace(out);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(out);
|
||||
} catch (RuntimeException e) {
|
||||
// swalow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package tested.robots;
|
||||
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* @author Flemming N. Larsen (original)
|
||||
*/
|
||||
public class ConstructorAwtAttack extends robocode.AdvancedRobot {
|
||||
|
||||
public ConstructorAwtAttack() {
|
||||
awtAttack();
|
||||
}
|
||||
|
||||
private void awtAttack() {
|
||||
try {
|
||||
Runnable doHack = new Runnable() {
|
||||
public void run() {
|
||||
writeAttack();
|
||||
|
||||
JFrame frame;
|
||||
|
||||
frame = new JFrame();
|
||||
frame.setName("Hack");
|
||||
frame.setVisible(true);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
javax.swing.SwingUtilities.invokeLater(doHack);
|
||||
} catch (RuntimeException e) {
|
||||
// swallow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAttack() {
|
||||
FileOutputStream fs;
|
||||
|
||||
try {
|
||||
fs = new FileOutputStream("C:\\Robocode.attack");
|
||||
fs.write(0xBA);
|
||||
fs.write(0xDF);
|
||||
fs.write(0x00);
|
||||
fs.write(0xD0);
|
||||
fs.close();
|
||||
out.println("Hacked!!!");
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace(out);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(out);
|
||||
} catch (RuntimeException e) {
|
||||
// swallow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package tested.robots;
|
||||
|
||||
|
||||
/**
|
||||
* @author Flemming N. Larsen (original)
|
||||
*/
|
||||
public class ConstructorThreadAttack extends robocode.AdvancedRobot {
|
||||
|
||||
public ConstructorThreadAttack() {
|
||||
runAttack();
|
||||
runAttack2();
|
||||
}
|
||||
|
||||
private void runAttack() {
|
||||
try {
|
||||
Attacker a = new Attacker();
|
||||
Thread t = new Thread(a);
|
||||
|
||||
t.start();
|
||||
} catch (RuntimeException e) {
|
||||
// swallow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
private void runAttack2() {
|
||||
try {
|
||||
Attacker a = new Attacker();
|
||||
ThreadGroup tg = new ThreadGroup("MyAttack");
|
||||
|
||||
tg.setMaxPriority(10);
|
||||
Thread t = new Thread(tg, a);
|
||||
|
||||
t.start();
|
||||
} catch (RuntimeException e) {
|
||||
// swallow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
private class Attacker implements Runnable {
|
||||
|
||||
public synchronized void run() {
|
||||
if (Thread.currentThread().getPriority() > 4) {
|
||||
out.println("Priority attack");
|
||||
}
|
||||
runAttack2();
|
||||
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package tested.robots;
|
||||
|
||||
|
||||
import robocode.AdvancedRobot;
|
||||
import robocode.ScannedRobotEvent;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
public class FileAttack extends AdvancedRobot {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// noinspection InfiniteLoopStatement
|
||||
for (;;) {
|
||||
turnLeft(100);
|
||||
ahead(10);
|
||||
turnLeft(100);
|
||||
back(10);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScannedRobot(ScannedRobotEvent event) {
|
||||
readAttack();
|
||||
writeAttack();
|
||||
}
|
||||
|
||||
private void readAttack() {
|
||||
try {
|
||||
FileInputStream fs = new FileInputStream("C:\\MSDOS.SYS");
|
||||
|
||||
System.out.print(fs.read());
|
||||
System.out.print(fs.read());
|
||||
System.out.print(fs.read());
|
||||
System.out.print(fs.read());
|
||||
fs.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace(out);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(out);
|
||||
} catch (RuntimeException e) {
|
||||
// swalow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAttack() {
|
||||
FileOutputStream fs;
|
||||
|
||||
try {
|
||||
fs = new FileOutputStream("C:\\Robocode.attack");
|
||||
fs.write(0xBA);
|
||||
fs.write(0xDF);
|
||||
fs.write(0x00);
|
||||
fs.write(0xD0);
|
||||
fs.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace(out);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(out);
|
||||
} catch (RuntimeException e) {
|
||||
// swalow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package tested.robots;
|
||||
|
||||
|
||||
import net.sf.robocode.security.HiddenAccess;
|
||||
import robocode.AdvancedRobot;
|
||||
import robocode.ScannedRobotEvent;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
public class IncludeNamespaceAttack extends AdvancedRobot {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// noinspection InfiniteLoopStatement
|
||||
for (;;) {
|
||||
turnLeft(100);
|
||||
ahead(10);
|
||||
turnLeft(100);
|
||||
back(10);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScannedRobot(ScannedRobotEvent event) {
|
||||
namespaceAttack();
|
||||
}
|
||||
|
||||
private void namespaceAttack() {
|
||||
try {
|
||||
HiddenAccess.createRules(10, 10, 10, 10, 1, false, 100);
|
||||
} catch (RuntimeException e) {
|
||||
// Swallow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package tested.robots;
|
||||
|
||||
|
||||
import robocode.AdvancedRobot;
|
||||
import robocode.ScannedRobotEvent;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
public class ThreadAttack extends AdvancedRobot {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// noinspection InfiniteLoopStatement
|
||||
for (;;) {
|
||||
turnLeft(100);
|
||||
ahead(10);
|
||||
turnLeft(100);
|
||||
back(10);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScannedRobot(ScannedRobotEvent event) {
|
||||
runAttack();
|
||||
runAttack2();
|
||||
}
|
||||
|
||||
private void runAttack() {
|
||||
try {
|
||||
Attacker a = new Attacker();
|
||||
Thread t = new Thread(a);
|
||||
|
||||
t.start();
|
||||
} catch (RuntimeException e) {
|
||||
// swallow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
private void runAttack2() {
|
||||
try {
|
||||
Attacker a = new Attacker();
|
||||
ThreadGroup tg = new ThreadGroup("MyAttack");
|
||||
|
||||
tg.setMaxPriority(10);
|
||||
Thread t = new Thread(tg, a);
|
||||
|
||||
t.start();
|
||||
} catch (RuntimeException e) {
|
||||
// swallow security exception
|
||||
e.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
private class Attacker implements Runnable {
|
||||
|
||||
public synchronized void run() {
|
||||
final int id = counter.incrementAndGet();
|
||||
|
||||
out.println("Running id:" + id);
|
||||
|
||||
if (Thread.currentThread().getPriority() > 4) {
|
||||
out.println("Priority attack");
|
||||
}
|
||||
runAttack2();
|
||||
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
out.println("Interrupted id:" + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package tested.robots;
|
||||
|
||||
|
||||
import robocode.*;
|
||||
|
||||
|
||||
/**
|
||||
* This nasty robot tries to interrupt the thread of each opponent robot that it scans.
|
||||
* It enumerates the threads recursively of thread group that is a parent of its own
|
||||
* thread group to find out, which threads that are active. These threads are all robot
|
||||
* threads.
|
||||
*
|
||||
* This robot is inspired by the hacker.Destroyer 1.3, which proved a security breach in
|
||||
* Robocode 1.7.2.1 Beta. The security breach was reported with:
|
||||
* Bug [3021140] Possible for robot to kill other robot threads.
|
||||
*
|
||||
* The security manager of Robocode must make sure that unsafe (robot) threads cannot
|
||||
* access thread groups other than its own thread group within checkAccess(Thread).
|
||||
*
|
||||
* @author Flemming N. Larsen (original)
|
||||
*/
|
||||
public class ThreadGroupAttack extends Robot {
|
||||
private Thread[] threads = new Thread[100];
|
||||
|
||||
public void run() {
|
||||
runAttack();
|
||||
|
||||
while (true) {
|
||||
turnGunLeft(30);
|
||||
}
|
||||
}
|
||||
|
||||
private void runAttack() {
|
||||
try {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
ThreadGroup parentGroup = Thread.currentThread().getThreadGroup().getParent();
|
||||
|
||||
while (true) {
|
||||
parentGroup.enumerate(threads, true);
|
||||
try {
|
||||
Thread.sleep(0);
|
||||
} catch (InterruptedException ignore) {}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
} catch (RuntimeException t) {
|
||||
t.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
public void onScannedRobot(ScannedRobotEvent e) {
|
||||
attackRobotThread(e.getName());
|
||||
}
|
||||
|
||||
private void attackRobotThread(String robotName) {
|
||||
for (Thread t : threads) {
|
||||
if (t != null && robotName.equals(t.getName())) {
|
||||
t.interrupt();
|
||||
out.println("Interrupted: " + robotName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package tested.robots;
|
||||
|
||||
|
||||
import robocode.AdvancedRobot;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
public class UndeadThread extends AdvancedRobot {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
out.println("I will live forever!");
|
||||
// noinspection InfiniteLoopStatement
|
||||
while (true) {
|
||||
try {
|
||||
body();
|
||||
} catch (RuntimeException t) {
|
||||
// spamming the console
|
||||
out.println("Swalowed it, HA HA HA HA HAAAAA !!!!!");
|
||||
out.println(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void body() {
|
||||
// noinspection InfiniteLoopStatement
|
||||
for (;;) {
|
||||
turnLeft(100);
|
||||
ahead(10);
|
||||
turnLeft(100);
|
||||
back(10);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.test.helpers;
|
||||
|
||||
|
||||
import robocode.util.Utils;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
public class Assert extends org.junit.Assert {
|
||||
public static void assertNear(double v1, double v2) {
|
||||
org.junit.Assert.assertEquals(v1, v2, Utils.NEAR_DELTA);
|
||||
}
|
||||
|
||||
public static void allAssertNear(double v1, double v2) {
|
||||
try {
|
||||
assertNear(v1, v2);
|
||||
} catch (RuntimeException ex) {
|
||||
ex.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void allAssertThat(T t, org.hamcrest.Matcher<T> tMatcher) {
|
||||
try {
|
||||
org.junit.Assert.assertThat(t, tMatcher);
|
||||
} catch (RuntimeException ex) {
|
||||
ex.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,485 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.ui.editor;
|
||||
|
||||
|
||||
import net.sf.robocode.io.FileUtil;
|
||||
import net.sf.robocode.io.Logger;
|
||||
import net.sf.robocode.repository.IRepositoryManager;
|
||||
import net.sf.robocode.ui.editor.theme.EditorThemeProperties;
|
||||
import net.sf.robocode.ui.editor.theme.EditorThemePropertiesManager;
|
||||
import net.sf.robocode.ui.editor.theme.EditorThemePropertyChangeAdapter;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.InternalFrameAdapter;
|
||||
import javax.swing.event.InternalFrameEvent;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
|
||||
/**
|
||||
* @author Mathew A. Nelson (original)
|
||||
* @author Flemming N. Larsen (contributor)
|
||||
* @author Matthew Reeder (contributor)
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EditWindow extends JInternalFrame {
|
||||
|
||||
private String fileName;
|
||||
private String robotName;
|
||||
public boolean modified;
|
||||
private final RobocodeEditor editor;
|
||||
private final IRepositoryManager repositoryManager;
|
||||
private final File robotsDirectory;
|
||||
private EditorPanel editorPanel;
|
||||
private EditorPane editorPane;
|
||||
|
||||
public EditWindow(IRepositoryManager repositoryManager, RobocodeEditor editor, File robotsDirectory) {
|
||||
super("Edit Window", true, true, true, true);
|
||||
this.editor = editor;
|
||||
this.robotsDirectory = robotsDirectory;
|
||||
this.repositoryManager = repositoryManager;
|
||||
initialize();
|
||||
}
|
||||
|
||||
public EditorPane getEditorPane() {
|
||||
if (editorPane == null) {
|
||||
editorPane = editorPanel.getEditorPane();
|
||||
InputMap im = editorPane.getInputMap();
|
||||
|
||||
// FIXME: Replace hack with better solution than using 'ctrl H'
|
||||
im.put(KeyStroke.getKeyStroke("ctrl H"), editor.getReplaceAction());
|
||||
}
|
||||
return editorPane;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String getRobotName() {
|
||||
return robotName;
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
try {
|
||||
addInternalFrameListener(new InternalFrameAdapter() {
|
||||
@Override
|
||||
public void internalFrameClosing(InternalFrameEvent e) {
|
||||
if (!modified || fileSave(true)) {
|
||||
editor.setLineStatus(-1);
|
||||
dispose();
|
||||
}
|
||||
editor.removeFromWindowMenu(EditWindow.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalFrameDeactivated(InternalFrameEvent e) {
|
||||
editor.setLineStatus(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalFrameIconified(InternalFrameEvent e) {
|
||||
editor.setLineStatus(-1);
|
||||
}
|
||||
});
|
||||
setFrameIcon(new ImageIcon(EditWindow.class.getResource("/net/sf/robocode/ui/icons/robocode-icon.png")));
|
||||
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
|
||||
setSize(750, 500);
|
||||
|
||||
editor.addToWindowMenu(this);
|
||||
|
||||
editorPanel = new EditorPanel();
|
||||
setContentPane(editorPanel);
|
||||
|
||||
EditorThemeProperties currentThemeProps = EditorThemePropertiesManager.getCurrentEditorThemeProperties();
|
||||
Font font = currentThemeProps.getFont();
|
||||
editorPanel.setFont(font);
|
||||
|
||||
// Make sure the source editor window gets focus with a blinking cursor
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
editorPanel.getEditorPane().requestFocus();
|
||||
}
|
||||
});
|
||||
|
||||
EditorThemePropertiesManager.addListener(new EditorThemePropertyChangeAdapter() {
|
||||
@Override
|
||||
public void onFontChanged(Font newFont) {
|
||||
editorPanel.setFont(newFont);
|
||||
}
|
||||
});
|
||||
|
||||
final JavaDocument document = (JavaDocument) editorPanel.getEditorPane().getDocument();
|
||||
|
||||
document.addDocumentListener(new DocumentListener() {
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
updateModificationState();
|
||||
}
|
||||
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
updateModificationState();
|
||||
}
|
||||
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
updateModificationState();
|
||||
}
|
||||
|
||||
// Bug-361 Problem in the text editor related with the .java file modification
|
||||
private void updateModificationState() {
|
||||
setModified(editorPanel.getEditorPane().isModified());
|
||||
}
|
||||
});
|
||||
} catch (RuntimeException e) {
|
||||
Logger.logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFileName(String newFileName) {
|
||||
fileName = newFileName;
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
public void setRobotName(String newRobotName) {
|
||||
robotName = newRobotName;
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
private void updateTitle() {
|
||||
StringBuffer titleBuf = new StringBuffer("Editing");
|
||||
if (fileName != null) {
|
||||
titleBuf.append(" - ").append(fileName);
|
||||
} else if (robotName != null) {
|
||||
titleBuf.append(" - ").append(robotName);
|
||||
}
|
||||
if (modified) {
|
||||
titleBuf.append(" *");
|
||||
}
|
||||
setTitle(titleBuf.toString());
|
||||
}
|
||||
|
||||
private void setModified(boolean modified) {
|
||||
boolean updated = (modified != this.modified);
|
||||
if (updated) {
|
||||
this.modified = modified;
|
||||
updateTitle();
|
||||
editor.setSaveFileMenuItemsEnabled(modified);
|
||||
}
|
||||
}
|
||||
|
||||
public void compile() {
|
||||
if (!fileSave(true, true)) {
|
||||
error("You must save before compiling.");
|
||||
return;
|
||||
}
|
||||
if (editor.getCompiler() != null) {
|
||||
// The compiler + refresh of the repository is done in a thread in order to avoid the compiler
|
||||
// window hanging while compiling. The SwingUtilities.invokeLater() does not do a good job here
|
||||
// (window is still hanging). Hence, a real thread running beside the EDT is used, which does a
|
||||
// great job, where each each new print from the compiler is written out as soon as it is ready
|
||||
// in the output stream.
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
if (fileName == null) {
|
||||
error("You must save before compiling.");
|
||||
return;
|
||||
}
|
||||
editor.getCompiler().compile(getRobotDir(), fileName);
|
||||
repositoryManager.refresh(fileName);
|
||||
}
|
||||
}).start();
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(editor, "No compiler installed.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void error(String msg) {
|
||||
Object[] options = {
|
||||
"OK"
|
||||
};
|
||||
|
||||
JOptionPane.showOptionDialog(this, msg, "Error", JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null,
|
||||
options, options[0]);
|
||||
}
|
||||
|
||||
public boolean fileSave(boolean confirm) {
|
||||
return fileSave(confirm, false);
|
||||
}
|
||||
|
||||
private boolean fileSave(boolean confirm, boolean mustSave) {
|
||||
if (confirm) {
|
||||
if (!modified) {
|
||||
return true;
|
||||
}
|
||||
String s = fileName;
|
||||
|
||||
if (s == null) {
|
||||
s = robotName;
|
||||
}
|
||||
if (s == null) {
|
||||
s = "This file";
|
||||
}
|
||||
int ok = JOptionPane.showConfirmDialog(this, s + " has been modified. Do you wish to save it?",
|
||||
"Modified file", JOptionPane.YES_NO_CANCEL_OPTION);
|
||||
|
||||
if (ok == JOptionPane.NO_OPTION) {
|
||||
return !mustSave;
|
||||
}
|
||||
if (ok == JOptionPane.CANCEL_OPTION) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
String fileName = getFileName();
|
||||
|
||||
if (fileName == null) {
|
||||
return fileSaveAs();
|
||||
}
|
||||
|
||||
String reasonableFilename = getReasonableFilename();
|
||||
|
||||
if (reasonableFilename != null) {
|
||||
try {
|
||||
String a = new File(reasonableFilename).getCanonicalPath();
|
||||
String b = new File(fileName).getCanonicalPath();
|
||||
|
||||
if (!a.equals(b)) {
|
||||
int ok = JOptionPane.showConfirmDialog(this,
|
||||
fileName + " should be saved in: \n" + reasonableFilename
|
||||
+ "\n Would you like to save it there instead?",
|
||||
"Name has changed",
|
||||
JOptionPane.YES_NO_CANCEL_OPTION);
|
||||
|
||||
if (ok == JOptionPane.CANCEL_OPTION) {
|
||||
return false;
|
||||
}
|
||||
if (ok == JOptionPane.YES_OPTION) {
|
||||
return fileSaveAs();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Logger.logError("Unable to check reasonable filename: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
BufferedWriter bufferedWriter = null;
|
||||
OutputStreamWriter outputStreamWriter = null;
|
||||
FileOutputStream fileOutputStream = null;
|
||||
|
||||
try {
|
||||
fileOutputStream = new FileOutputStream(fileName);
|
||||
outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF8");
|
||||
bufferedWriter = new BufferedWriter(outputStreamWriter);
|
||||
|
||||
getEditorPane().write(bufferedWriter);
|
||||
setModified(false);
|
||||
} catch (IOException e) {
|
||||
error("Cannot write file: " + e);
|
||||
return false;
|
||||
} finally {
|
||||
FileUtil.cleanupStream(bufferedWriter);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getRobotDir() {
|
||||
String saveDir = robotsDirectory.getPath() + File.separatorChar;
|
||||
|
||||
String text = getEditorPane().getText();
|
||||
int pIndex = text.indexOf("package ");
|
||||
|
||||
if (pIndex >= 0) {
|
||||
int pEIndex = text.indexOf(";", pIndex);
|
||||
|
||||
if (pEIndex > 0) {
|
||||
String packageTree = text.substring(pIndex + 8, pEIndex) + File.separatorChar;
|
||||
|
||||
packageTree = packageTree.replace('.', File.separatorChar);
|
||||
|
||||
saveDir += packageTree;
|
||||
}
|
||||
}
|
||||
return saveDir;
|
||||
}
|
||||
|
||||
public boolean fileSaveAs() {
|
||||
String javaFileName = null;
|
||||
String saveDir = getRobotDir();
|
||||
|
||||
String text = getEditorPane().getText();
|
||||
|
||||
int pIndex = text.indexOf("public class ");
|
||||
|
||||
if (pIndex >= 0) {
|
||||
int pEIndex = text.indexOf(" ", pIndex + 13);
|
||||
|
||||
if (pEIndex > 0) {
|
||||
int pEIndex2 = text.indexOf("\n", pIndex + 13);
|
||||
|
||||
if (pEIndex2 > 0 && pEIndex2 < pEIndex) {
|
||||
pEIndex = pEIndex2;
|
||||
}
|
||||
javaFileName = text.substring(pIndex + 13, pEIndex).trim() + ".java";
|
||||
} else {
|
||||
pEIndex = text.indexOf("\n", pIndex + 13);
|
||||
if (pEIndex > 0) {
|
||||
javaFileName = text.substring(pIndex + 13, pEIndex).trim() + ".java";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File f = new File(saveDir);
|
||||
|
||||
if (!f.exists()) {
|
||||
int ok = JOptionPane.showConfirmDialog(this,
|
||||
"Your robot should be saved in the directory: " + saveDir
|
||||
+ "\nThis directory does not exist, would you like to create it?",
|
||||
"Create Directory",
|
||||
JOptionPane.YES_NO_CANCEL_OPTION);
|
||||
|
||||
if (ok == JOptionPane.YES_OPTION) {
|
||||
if (!f.exists() && !f.mkdirs()) {
|
||||
Logger.logError("Cannot create: " + f);
|
||||
}
|
||||
f = new File(saveDir);
|
||||
}
|
||||
if (ok == JOptionPane.CANCEL_OPTION) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JFileChooser chooser;
|
||||
|
||||
chooser = new JFileChooser(f);
|
||||
chooser.setCurrentDirectory(f);
|
||||
|
||||
FileFilter filter = new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
if (pathname.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
String fn = pathname.getName();
|
||||
int idx = fn.lastIndexOf('.');
|
||||
String extension = "";
|
||||
|
||||
if (idx >= 0) {
|
||||
extension = fn.substring(idx);
|
||||
}
|
||||
return extension.equalsIgnoreCase(".java");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Robots";
|
||||
}
|
||||
};
|
||||
|
||||
chooser.setFileFilter(filter);
|
||||
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
done = true;
|
||||
if (javaFileName != null) {
|
||||
chooser.setSelectedFile(new File(f, javaFileName));
|
||||
}
|
||||
int rv = chooser.showSaveDialog(this);
|
||||
String robotFileName;
|
||||
|
||||
if (rv == JFileChooser.APPROVE_OPTION) {
|
||||
robotFileName = chooser.getSelectedFile().getPath();
|
||||
File outFile = new File(robotFileName);
|
||||
|
||||
if (outFile.exists()) {
|
||||
int ok = JOptionPane.showConfirmDialog(this,
|
||||
robotFileName + " already exists. Are you sure you want to replace it?", "Warning",
|
||||
JOptionPane.YES_NO_CANCEL_OPTION);
|
||||
|
||||
if (ok == JOptionPane.NO_OPTION) {
|
||||
done = false;
|
||||
continue;
|
||||
}
|
||||
if (ok == JOptionPane.CANCEL_OPTION) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
setFileName(robotFileName);
|
||||
fileSave(false);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getPackage() {
|
||||
String text = getEditorPane().getText();
|
||||
int pIndex = text.indexOf("package ");
|
||||
|
||||
if (pIndex >= 0) {
|
||||
int pEIndex = text.indexOf(";", pIndex);
|
||||
|
||||
if (pEIndex > 0) {
|
||||
return text.substring(pIndex + 8, pEIndex);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String getReasonableFilename() {
|
||||
StringBuffer fileName = new StringBuffer(robotsDirectory.getPath()).append(File.separatorChar);
|
||||
String javaFileName;
|
||||
String packageTree = null;
|
||||
|
||||
String text = getEditorPane().getText();
|
||||
StringTokenizer tokenizer = new StringTokenizer(text, " \t\r\n;");
|
||||
String token;
|
||||
boolean inComment = false;
|
||||
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
token = tokenizer.nextToken();
|
||||
if (!inComment && (token.equals("/*") || token.equals("/**"))) {
|
||||
inComment = true;
|
||||
}
|
||||
if (inComment && (token.equals("*/") || token.equals("**/"))) {
|
||||
inComment = false;
|
||||
}
|
||||
if (inComment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packageTree == null && token.equals("package")) {
|
||||
packageTree = tokenizer.nextToken();
|
||||
if (packageTree == null || packageTree.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
packageTree = packageTree.replace('.', File.separatorChar);
|
||||
packageTree += File.separator;
|
||||
fileName.append(packageTree);
|
||||
}
|
||||
if (token.equals("class")) {
|
||||
javaFileName = tokenizer.nextToken() + ".java";
|
||||
fileName.append(javaFileName);
|
||||
return fileName.toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.ui.editor;
|
||||
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* Customized JMenuItem where each item is bound to a specific JInternalFrame,
|
||||
* so we can have a dynamic menu of open windows.
|
||||
*
|
||||
* @author Matthew Reeder (original)
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class WindowMenuItem extends JCheckBoxMenuItem implements ActionListener {
|
||||
|
||||
// Maximum number of windows that will be shown on the menu (to get the rest, you'll
|
||||
// have to open the dialog). The number 9 is also the number of most recently used
|
||||
// files that normally show up in other applications. The reason is so that you can
|
||||
// give them dynamic hotkeys from 1 to 9. Otherwise, there's no reason (besides
|
||||
// avoiding taking up way too much space) to limit the size of the menu.
|
||||
public static final int WINDOW_MENU_MAX_SIZE = 9;
|
||||
// Number of static menu items before the dynamic menu (including seperators)
|
||||
public static final int PRECEDING_WINDOW_MENU_ITEMS = 3;
|
||||
// Number of static menu items after the dynamic menu (including seperators
|
||||
// and the More Windows... menu item)
|
||||
public static final int SUBSEQUENT_WINDOW_MENU_ITEMS = 1;
|
||||
// Normal max length of a window name
|
||||
public static final int MAX_WINDOW_NAME_LENGTH = 30;
|
||||
// I make one "special" menu item that isn't tied to a window. Since it has
|
||||
// similar needs for enabling/visibility and labeling, I made it the same class.
|
||||
public static final int REGULAR_WINDOW = 0, SPECIAL_MORE = 2;
|
||||
private EditWindow window;
|
||||
private JMenu parentMenu;
|
||||
private final int type;
|
||||
|
||||
public WindowMenuItem(EditWindow window, JMenu parentMenu) {
|
||||
super();
|
||||
this.window = window;
|
||||
this.parentMenu = parentMenu;
|
||||
type = REGULAR_WINDOW;
|
||||
parentMenu.add(this, parentMenu.getMenuComponentCount() - SUBSEQUENT_WINDOW_MENU_ITEMS);
|
||||
addActionListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* WindowMenuItem Constructor for "More Windows..." menu.
|
||||
*/
|
||||
public WindowMenuItem() {
|
||||
type = SPECIAL_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for the menu item
|
||||
* <p/>
|
||||
* Brings the window to the front. This should be called for the "More
|
||||
* Windows..." Item, because it doesn't make itself its own ActionListener.
|
||||
* <p/>
|
||||
* Note that e can be null, and this menu item might not be showing (if this
|
||||
* is called from the "More Windows" dialog).
|
||||
*/
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (window.isIcon()) {
|
||||
try {
|
||||
window.setIcon(false);
|
||||
} catch (RuntimeException ignored) {}
|
||||
}
|
||||
if (window.getDesktopPane() != null) {
|
||||
window.getDesktopPane().setSelectedFrame(window);
|
||||
}
|
||||
window.toFront();
|
||||
window.grabFocus();
|
||||
try {
|
||||
window.setSelected(true);
|
||||
} catch (RuntimeException ignored) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label that should be used. If the menu item is supposed to be
|
||||
* hidden, this may not be a real valid label.
|
||||
*/
|
||||
@Override
|
||||
public String getText() {
|
||||
if (type == SPECIAL_MORE) {
|
||||
Container parent = getParent();
|
||||
|
||||
if (parent == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
int numWindows = parent.getComponentCount() - PRECEDING_WINDOW_MENU_ITEMS - SUBSEQUENT_WINDOW_MENU_ITEMS;
|
||||
|
||||
if (numWindows <= 0) {
|
||||
return "No Windows Open";
|
||||
}
|
||||
|
||||
return "More Windows...";
|
||||
}
|
||||
if (window == null || parentMenu == null) {
|
||||
return "";
|
||||
}
|
||||
String text = (getIndex() + 1) + " " + getFileName();
|
||||
|
||||
if (window.modified) {
|
||||
text += " *";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
protected String getFileName() {
|
||||
if (window.getFileName() == null) {
|
||||
return "Untitled " + (getPrecedingNewFiles() + 1);
|
||||
}
|
||||
|
||||
String name = window.getFileName();
|
||||
|
||||
if (name.length() < MAX_WINDOW_NAME_LENGTH) {
|
||||
return name;
|
||||
}
|
||||
if (name.indexOf(File.separatorChar) < 0) {
|
||||
return name;
|
||||
} // If there are no separators, I can't really intelligently truncate.
|
||||
int startLength = name.indexOf(File.separatorChar, 1) + 1;
|
||||
int endLength = name.length() - name.lastIndexOf(File.separatorChar);
|
||||
|
||||
if (endLength + startLength + 3 > name.length()) {
|
||||
return name;
|
||||
} // return name anyways, since we're not getting it any shorter.
|
||||
|
||||
boolean change;
|
||||
|
||||
do {
|
||||
change = false;
|
||||
int newEndLength = name.length() - name.lastIndexOf(File.separatorChar, name.length() - endLength - 1);
|
||||
|
||||
if (newEndLength + startLength + 3 <= MAX_WINDOW_NAME_LENGTH) {
|
||||
endLength = newEndLength;
|
||||
change = true;
|
||||
}
|
||||
int newStartLength = name.indexOf(File.separatorChar, startLength + 1) + 1;
|
||||
|
||||
if (endLength + startLength + 3 <= MAX_WINDOW_NAME_LENGTH) {
|
||||
startLength = newStartLength;
|
||||
change = true;
|
||||
}
|
||||
} while (change);
|
||||
|
||||
return name.substring(0, startLength) + "..." + name.substring(name.length() - endLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return how many nameless windows occur before this one in the parent.
|
||||
*/
|
||||
protected int getPrecedingNewFiles() {
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < WINDOW_MENU_MAX_SIZE
|
||||
&& i < parentMenu.getMenuComponentCount() - PRECEDING_WINDOW_MENU_ITEMS - SUBSEQUENT_WINDOW_MENU_ITEMS
|
||||
&& parentMenu.getMenuComponent(i + PRECEDING_WINDOW_MENU_ITEMS) != this; i++) {
|
||||
if (parentMenu.getMenuComponent(i + PRECEDING_WINDOW_MENU_ITEMS) instanceof WindowMenuItem
|
||||
&& ((WindowMenuItem) parentMenu.getMenuComponent(i + PRECEDING_WINDOW_MENU_ITEMS)).window.getFileName()
|
||||
== null) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figures out what index (from 0 to WINDOW_MENU_MAX_SIZE-1) this item is in
|
||||
* the window menu.
|
||||
* <p/>
|
||||
* @return -1 if this item isn't showing.
|
||||
*/
|
||||
protected int getIndex() {
|
||||
for (int i = 0; i < WINDOW_MENU_MAX_SIZE
|
||||
&& i < parentMenu.getMenuComponentCount() - PRECEDING_WINDOW_MENU_ITEMS - SUBSEQUENT_WINDOW_MENU_ITEMS; i++) {
|
||||
if (this == parentMenu.getMenuComponent(i + PRECEDING_WINDOW_MENU_ITEMS)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the character in the label that should be underlined
|
||||
*/
|
||||
@Override
|
||||
public int getDisplayedMnemonicIndex() {
|
||||
return (type == SPECIAL_MORE) ? 11 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keyboard mnemonic for this item, which is the virtual key
|
||||
* code for its 1-based index.
|
||||
*/
|
||||
@Override
|
||||
public int getMnemonic() {
|
||||
return (type == SPECIAL_MORE) ? KeyEvent.VK_S : KeyEvent.VK_1 + getIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this item should be showing.
|
||||
* <p/>
|
||||
* Returns false if there are more than WINDOW_MENU_MAX_SIZE items before it
|
||||
* in the menu.
|
||||
*/
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
if (type == SPECIAL_MORE) {
|
||||
Container parent = getParent();
|
||||
|
||||
if (parent == null) {
|
||||
return true;
|
||||
}
|
||||
int numWindows = parent.getComponentCount() - PRECEDING_WINDOW_MENU_ITEMS - SUBSEQUENT_WINDOW_MENU_ITEMS;
|
||||
|
||||
updateSelection();
|
||||
return (numWindows <= 0) || (numWindows > WINDOW_MENU_MAX_SIZE);
|
||||
}
|
||||
updateSelection();
|
||||
return getIndex() >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this item should be enabled (selectable).
|
||||
* <p/>
|
||||
* Returns false if it is a More Windows... item and there are no windows.
|
||||
*/
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
if (type == SPECIAL_MORE) {
|
||||
Container parent = getParent();
|
||||
|
||||
if (parent == null) {
|
||||
return true;
|
||||
}
|
||||
int numWindows = parent.getComponentCount() - PRECEDING_WINDOW_MENU_ITEMS - SUBSEQUENT_WINDOW_MENU_ITEMS;
|
||||
|
||||
return (numWindows > 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this menu item should currently show as "selected".
|
||||
* <p/>
|
||||
* The item should be seleced if the window it's tied to has focus.
|
||||
*/
|
||||
@Override
|
||||
public boolean isSelected() {
|
||||
return (type != SPECIAL_MORE) && (window != null && window.getDesktopPane() != null)
|
||||
&& window.getDesktopPane().getSelectedFrame() == window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the underlying menu item knows if we're selected.
|
||||
*/
|
||||
public void updateSelection() {
|
||||
setSelected(isSelected()); // Sort of a silly thing to do...
|
||||
setEnabled(isEnabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the EditWindow that this menu item is tied to.
|
||||
*/
|
||||
public EditWindow getEditWindow() {
|
||||
return window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of this object.
|
||||
* <p/>
|
||||
* Handy for repurposing the menu items as list items :-)
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return (type == SPECIAL_MORE) ? "" : getFileName();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,632 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.ui;
|
||||
|
||||
|
||||
import net.sf.robocode.battle.BattleProperties;
|
||||
import net.sf.robocode.battle.BattleResultsTableModel;
|
||||
import net.sf.robocode.battle.IBattleManager;
|
||||
import net.sf.robocode.core.Container;
|
||||
import net.sf.robocode.host.ICpuManager;
|
||||
import net.sf.robocode.io.FileUtil;
|
||||
import net.sf.robocode.repository.IRepositoryManager;
|
||||
import net.sf.robocode.settings.ISettingsManager;
|
||||
import net.sf.robocode.ui.battle.AwtBattleAdaptor;
|
||||
import net.sf.robocode.ui.dialog.*;
|
||||
import net.sf.robocode.ui.packager.RobotPackager;
|
||||
import net.sf.robocode.ui.editor.IRobocodeEditor;
|
||||
import net.sf.robocode.version.IVersionManager;
|
||||
import robocode.control.events.BattleCompletedEvent;
|
||||
import robocode.control.events.IBattleListener;
|
||||
import robocode.control.snapshot.ITurnSnapshot;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
/**
|
||||
* @author Mathew A. Nelson (original)
|
||||
* @author Flemming N. Larsen (contributor)
|
||||
* @author Luis Crespo (contributor)
|
||||
*/
|
||||
public class WindowManager implements IWindowManagerExt {
|
||||
|
||||
private final static int TIMER_TICKS_PER_SECOND = 50;
|
||||
private final AwtBattleAdaptor awtAdaptor;
|
||||
private RobotPackager robotPackager;
|
||||
private RobotExtractor robotExtractor;
|
||||
private final ISettingsManager settingsManager;
|
||||
private final IBattleManager battleManager;
|
||||
private final ICpuManager cpuManager;
|
||||
private final IRepositoryManager repositoryManager;
|
||||
private final IVersionManager versionManager;
|
||||
private final IImageManager imageManager;
|
||||
private IRobotDialogManager robotDialogManager;
|
||||
private RobocodeFrame robocodeFrame;
|
||||
|
||||
private boolean isGUIEnabled = true;
|
||||
private boolean isSlave;
|
||||
private boolean centerRankings = true;
|
||||
private boolean oldRankingHideState = true;
|
||||
private boolean showResults = true;
|
||||
|
||||
public WindowManager(ISettingsManager settingsManager, IBattleManager battleManager, ICpuManager cpuManager, IRepositoryManager repositoryManager, IImageManager imageManager, IVersionManager versionManager) {
|
||||
this.settingsManager = settingsManager;
|
||||
this.battleManager = battleManager;
|
||||
this.repositoryManager = repositoryManager;
|
||||
this.cpuManager = cpuManager;
|
||||
this.versionManager = versionManager;
|
||||
this.imageManager = imageManager;
|
||||
awtAdaptor = new AwtBattleAdaptor(battleManager, TIMER_TICKS_PER_SECOND, true);
|
||||
|
||||
// we will set UI better priority than robots and battle have
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 2);
|
||||
} catch (SecurityException ex) {// that's a pity
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setBusyPointer(boolean enabled) {
|
||||
robocodeFrame.setBusyPointer(enabled);
|
||||
}
|
||||
|
||||
public synchronized void addBattleListener(IBattleListener listener) {
|
||||
awtAdaptor.addListener(listener);
|
||||
}
|
||||
|
||||
public synchronized void removeBattleListener(IBattleListener listener) {
|
||||
awtAdaptor.removeListener(listener);
|
||||
}
|
||||
|
||||
public boolean isGUIEnabled() {
|
||||
return isGUIEnabled;
|
||||
}
|
||||
|
||||
public void setEnableGUI(boolean enable) {
|
||||
isGUIEnabled = enable;
|
||||
|
||||
// Set the system property so the AWT headless mode.
|
||||
// Read more about headless mode here:
|
||||
// http://java.sun.com/developer/technicalArticles/J2SE/Desktop/headless/
|
||||
System.setProperty("java.awt.headless", "" + !enable);
|
||||
}
|
||||
|
||||
public void setSlave(boolean value) {
|
||||
isSlave = value;
|
||||
}
|
||||
|
||||
public boolean isSlave() {
|
||||
return isSlave;
|
||||
}
|
||||
|
||||
public boolean isIconified() {
|
||||
return robocodeFrame.isIconified();
|
||||
}
|
||||
|
||||
public boolean isShowResultsEnabled() {
|
||||
return settingsManager.getOptionsCommonShowResults() && showResults;
|
||||
}
|
||||
|
||||
public void setEnableShowResults(boolean enable) {
|
||||
showResults = enable;
|
||||
}
|
||||
|
||||
public ITurnSnapshot getLastSnapshot() {
|
||||
return awtAdaptor.getLastSnapshot();
|
||||
}
|
||||
|
||||
public int getFPS() {
|
||||
return isIconified() ? 0 : awtAdaptor.getFPS();
|
||||
}
|
||||
|
||||
public RobocodeFrame getRobocodeFrame() {
|
||||
if (robocodeFrame == null) {
|
||||
this.robocodeFrame = Container.getComponent(RobocodeFrame.class);
|
||||
}
|
||||
return robocodeFrame;
|
||||
}
|
||||
|
||||
public void showRobocodeFrame(boolean visible, boolean iconified) {
|
||||
RobocodeFrame frame = getRobocodeFrame();
|
||||
|
||||
if (iconified) {
|
||||
frame.setState(Frame.ICONIFIED);
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
// Pack frame to size all components
|
||||
WindowUtil.packCenterShow(frame);
|
||||
|
||||
WindowUtil.setStatusLabel(frame.getStatusLabel());
|
||||
|
||||
frame.checkUpdateOnStart();
|
||||
|
||||
} else {
|
||||
frame.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void showAboutBox() {
|
||||
packCenterShow(Container.getComponent(AboutBox.class), true);
|
||||
}
|
||||
|
||||
public String showBattleOpenDialog(final String defExt, final String name) {
|
||||
JFileChooser chooser = new JFileChooser(battleManager.getBattlePath());
|
||||
|
||||
chooser.setFileFilter(
|
||||
new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return pathname.isDirectory()
|
||||
|| pathname.getName().toLowerCase().lastIndexOf(defExt.toLowerCase())
|
||||
== pathname.getName().length() - defExt.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return name;
|
||||
}
|
||||
});
|
||||
|
||||
if (chooser.showOpenDialog(getRobocodeFrame()) == JFileChooser.APPROVE_OPTION) {
|
||||
return chooser.getSelectedFile().getPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String saveBattleDialog(String path, final String defExt, final String name) {
|
||||
File f = new File(path);
|
||||
|
||||
JFileChooser chooser;
|
||||
|
||||
chooser = new JFileChooser(f);
|
||||
|
||||
javax.swing.filechooser.FileFilter filter = new javax.swing.filechooser.FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return pathname.isDirectory()
|
||||
|| pathname.getName().toLowerCase().lastIndexOf(defExt.toLowerCase())
|
||||
== pathname.getName().length() - defExt.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
chooser.setFileFilter(filter);
|
||||
int rv = chooser.showSaveDialog(getRobocodeFrame());
|
||||
String result = null;
|
||||
|
||||
if (rv == JFileChooser.APPROVE_OPTION) {
|
||||
result = chooser.getSelectedFile().getPath();
|
||||
int idx = result.lastIndexOf('.');
|
||||
String extension = "";
|
||||
|
||||
if (idx > 0) {
|
||||
extension = result.substring(idx);
|
||||
}
|
||||
if (!(extension.equalsIgnoreCase(defExt))) {
|
||||
result += defExt;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void showVersionsTxt() {
|
||||
showInBrowser("file://" + new File(FileUtil.getCwd(), "").getAbsoluteFile() + File.separator + "versions.md");
|
||||
}
|
||||
|
||||
public void showHelpApi() {
|
||||
showInBrowser(
|
||||
"file://" + new File(FileUtil.getCwd(), "").getAbsoluteFile() + File.separator + "javadoc" + File.separator
|
||||
+ "index.html");
|
||||
}
|
||||
|
||||
public void showReadMe() {
|
||||
showInBrowser("file://" + new File(FileUtil.getCwd(), "ReadMe.html").getAbsoluteFile());
|
||||
}
|
||||
|
||||
public void showFaq() {
|
||||
showInBrowser("http://robowiki.net/w/index.php?title=Robocode/FAQ");
|
||||
}
|
||||
|
||||
public void showOnlineHelp() {
|
||||
showInBrowser("http://robowiki.net/w/index.php?title=Robocode/Getting_Started");
|
||||
}
|
||||
|
||||
public void showJavaDocumentation() {
|
||||
showInBrowser("http://docs.oracle.com/javase/6/docs/api/");
|
||||
}
|
||||
|
||||
public void showRobocodeHome() {
|
||||
showInBrowser("http://robocode.sourceforge.net");
|
||||
}
|
||||
|
||||
public void showRoboWiki() {
|
||||
showInBrowser("http://robowiki.net");
|
||||
}
|
||||
|
||||
public void showGoogleGroupRobocode() {
|
||||
showInBrowser("https://groups.google.com/forum/?fromgroups#!forum/robocode");
|
||||
}
|
||||
|
||||
public void showRobocodeRepository() {
|
||||
showInBrowser("http://robocoderepository.com");
|
||||
}
|
||||
|
||||
public void showOptionsPreferences() {
|
||||
try {
|
||||
battleManager.pauseBattle();
|
||||
|
||||
WindowUtil.packCenterShow(getRobocodeFrame(), Container.getComponent(PreferencesDialog.class));
|
||||
} finally {
|
||||
battleManager.resumeIfPausedBattle(); // THIS is just dirty hack-fix of more complex problem with desiredTPS and pausing. resumeBattle() belongs here.
|
||||
}
|
||||
}
|
||||
|
||||
public void showResultsDialog(BattleCompletedEvent event) {
|
||||
final ResultsDialog dialog = Container.getComponent(ResultsDialog.class);
|
||||
|
||||
dialog.setup(event.getSortedResults(), event.getBattleRules().getNumRounds());
|
||||
packCenterShow(dialog, true);
|
||||
}
|
||||
|
||||
public void showRankingDialog(boolean visible) {
|
||||
boolean currentRankingHideState = settingsManager.getOptionsCommonDontHideRankings();
|
||||
|
||||
// Check if the Ranking hide states has changed
|
||||
if (currentRankingHideState != oldRankingHideState) {
|
||||
// Remove current visible RankingDialog, if it is there
|
||||
Container.getComponent(RankingDialog.class).dispose();
|
||||
|
||||
// Replace old RankingDialog, as the owner window must be replaced from the constructor
|
||||
Container.cache.removeComponent(RankingDialog.class);
|
||||
Container.cache.addComponent(RankingDialog.class);
|
||||
|
||||
// Reset flag for centering the dialog the first time it is shown
|
||||
centerRankings = true;
|
||||
}
|
||||
|
||||
RankingDialog rankingDialog = Container.getComponent(RankingDialog.class);
|
||||
|
||||
if (visible) {
|
||||
packCenterShow(rankingDialog, centerRankings);
|
||||
centerRankings = false; // only center the first time Rankings are shown
|
||||
} else {
|
||||
rankingDialog.dispose();
|
||||
}
|
||||
|
||||
// Save current Ranking hide state
|
||||
oldRankingHideState = currentRankingHideState;
|
||||
}
|
||||
|
||||
public void showRobocodeEditor() {
|
||||
JFrame editor = (JFrame) net.sf.robocode.core.Container.getComponent(IRobocodeEditor.class);
|
||||
|
||||
if (!editor.isVisible()) {
|
||||
WindowUtil.packCenterShow(editor);
|
||||
} else {
|
||||
editor.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void showRobotPackager() {
|
||||
if (robotPackager != null) {
|
||||
robotPackager.dispose();
|
||||
robotPackager = null;
|
||||
}
|
||||
|
||||
robotPackager = net.sf.robocode.core.Container.factory.getComponent(RobotPackager.class);
|
||||
WindowUtil.packCenterShow(robotPackager);
|
||||
}
|
||||
|
||||
public void showRobotExtractor(JFrame owner) {
|
||||
if (robotExtractor != null) {
|
||||
robotExtractor.dispose();
|
||||
robotExtractor = null;
|
||||
}
|
||||
|
||||
robotExtractor = new net.sf.robocode.ui.dialog.RobotExtractor(owner, this, repositoryManager);
|
||||
WindowUtil.packCenterShow(robotExtractor);
|
||||
}
|
||||
|
||||
public void showSplashScreen() {
|
||||
RcSplashScreen splashScreen = Container.getComponent(RcSplashScreen.class);
|
||||
|
||||
packCenterShow(splashScreen, true);
|
||||
|
||||
WindowUtil.setStatusLabel(splashScreen.getSplashLabel());
|
||||
|
||||
repositoryManager.reload(versionManager.isLastRunVersionChanged());
|
||||
|
||||
WindowUtil.setStatusLabel(splashScreen.getSplashLabel());
|
||||
cpuManager.getCpuConstant();
|
||||
|
||||
WindowUtil.setStatus("");
|
||||
WindowUtil.setStatusLabel(null);
|
||||
|
||||
splashScreen.dispose();
|
||||
}
|
||||
|
||||
public void showNewBattleDialog(BattleProperties battleProperties) {
|
||||
try {
|
||||
battleManager.pauseBattle();
|
||||
final NewBattleDialog battleDialog = Container.createComponent(NewBattleDialog.class);
|
||||
|
||||
battleDialog.setup(settingsManager, battleProperties);
|
||||
WindowUtil.packCenterShow(getRobocodeFrame(), battleDialog);
|
||||
} finally {
|
||||
battleManager.resumeBattle();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean closeRobocodeEditor() {
|
||||
IRobocodeEditor editor = net.sf.robocode.core.Container.getComponent(IRobocodeEditor.class);
|
||||
|
||||
return editor == null || !((JFrame) editor).isVisible() || editor.close();
|
||||
}
|
||||
|
||||
public void showCreateTeamDialog() {
|
||||
TeamCreator teamCreator = Container.getComponent(TeamCreator.class);
|
||||
|
||||
WindowUtil.packCenterShow(teamCreator);
|
||||
}
|
||||
|
||||
public void showImportRobotDialog() {
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
|
||||
chooser.setFileFilter(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
if (pathname.isHidden()) {
|
||||
return false;
|
||||
}
|
||||
if (pathname.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
String filename = pathname.getName();
|
||||
|
||||
if (filename.equals("robocode.jar")) {
|
||||
return false;
|
||||
}
|
||||
int idx = filename.lastIndexOf('.');
|
||||
|
||||
String extension = "";
|
||||
|
||||
if (idx >= 0) {
|
||||
extension = filename.substring(idx);
|
||||
}
|
||||
return extension.equalsIgnoreCase(".jar") || extension.equalsIgnoreCase(".zip");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Jar Files";
|
||||
}
|
||||
});
|
||||
|
||||
chooser.setDialogTitle("Select the robot .jar file to copy to " + repositoryManager.getRobotsDirectory());
|
||||
|
||||
if (chooser.showDialog(getRobocodeFrame(), "Import") == JFileChooser.APPROVE_OPTION) {
|
||||
File inputFile = chooser.getSelectedFile();
|
||||
String fileName = inputFile.getName();
|
||||
String extension = "";
|
||||
|
||||
int idx = fileName.lastIndexOf('.');
|
||||
|
||||
if (idx >= 0) {
|
||||
extension = fileName.substring(idx);
|
||||
}
|
||||
if (!extension.equalsIgnoreCase(".jar")) {
|
||||
fileName += ".jar";
|
||||
}
|
||||
File outputFile = new File(repositoryManager.getRobotsDirectory(), fileName);
|
||||
|
||||
if (inputFile.equals(outputFile)) {
|
||||
JOptionPane.showMessageDialog(getRobocodeFrame(),
|
||||
outputFile.getName() + " is already in the robots directory!");
|
||||
return;
|
||||
}
|
||||
if (outputFile.exists()) {
|
||||
if (JOptionPane.showConfirmDialog(getRobocodeFrame(), outputFile + " already exists. Overwrite?",
|
||||
"Warning", JOptionPane.YES_NO_OPTION)
|
||||
== JOptionPane.NO_OPTION) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (JOptionPane.showConfirmDialog(getRobocodeFrame(),
|
||||
"Robocode will now copy " + inputFile.getName() + " to " + outputFile.getParent(), "Import robot",
|
||||
JOptionPane.OK_CANCEL_OPTION)
|
||||
== JOptionPane.OK_OPTION) {
|
||||
try {
|
||||
FileUtil.copy(inputFile, outputFile);
|
||||
repositoryManager.refresh();
|
||||
JOptionPane.showMessageDialog(getRobocodeFrame(), "Robot imported successfully.");
|
||||
} catch (IOException e) {
|
||||
JOptionPane.showMessageDialog(getRobocodeFrame(), "Import failed: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a web page using the browser manager.
|
||||
*
|
||||
* @param url The URL of the web page
|
||||
*/
|
||||
private void showInBrowser(String url) {
|
||||
try {
|
||||
BrowserManager.openURL(url);
|
||||
} catch (IOException e) {
|
||||
JOptionPane.showMessageDialog(getRobocodeFrame(), e.getMessage(), "Unable to open browser!",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
public void showSaveResultsDialog(BattleResultsTableModel tableModel) {
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
|
||||
chooser.setFileFilter(new FileFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
if (pathname.isHidden()) {
|
||||
return false;
|
||||
}
|
||||
if (pathname.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
String filename = pathname.getName();
|
||||
int idx = filename.lastIndexOf('.');
|
||||
|
||||
String extension = "";
|
||||
|
||||
if (idx >= 0) {
|
||||
extension = filename.substring(idx);
|
||||
}
|
||||
return extension.equalsIgnoreCase(".csv");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Comma Separated Value (CSV) File Format";
|
||||
}
|
||||
});
|
||||
|
||||
chooser.setDialogTitle("Save battle results");
|
||||
|
||||
if (chooser.showSaveDialog(getRobocodeFrame()) == JFileChooser.APPROVE_OPTION) {
|
||||
|
||||
String filename = chooser.getSelectedFile().getPath();
|
||||
|
||||
if (!filename.endsWith(".csv")) {
|
||||
filename += ".csv";
|
||||
}
|
||||
|
||||
boolean append = settingsManager.getOptionsCommonAppendWhenSavingResults();
|
||||
|
||||
tableModel.saveToFile(filename, append);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs, centers, and shows the specified window on the screen.
|
||||
* @param window the window to pack, center, and show
|
||||
* @param center {@code true} if the window must be centered; {@code false} otherwise
|
||||
*/
|
||||
private void packCenterShow(Window window, boolean center) {
|
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
||||
window.pack();
|
||||
if (center) {
|
||||
window.setLocation((screenSize.width - window.getWidth()) / 2, (screenSize.height - window.getHeight()) / 2);
|
||||
}
|
||||
window.setVisible(true);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
if (isGUIEnabled()) {
|
||||
getRobocodeFrame().dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatus(String s) {
|
||||
WindowUtil.setStatus(s);
|
||||
}
|
||||
|
||||
public void messageWarning(String s) {
|
||||
WindowUtil.messageWarning(s);
|
||||
}
|
||||
|
||||
public IRobotDialogManager getRobotDialogManager() {
|
||||
if (robotDialogManager == null) {
|
||||
robotDialogManager = new RobotDialogManager();
|
||||
}
|
||||
return robotDialogManager;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
setLookAndFeel();
|
||||
imageManager.initialize(); // Make sure this one is initialized so all images are available
|
||||
awtAdaptor.subscribe(isGUIEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Look and Feel (LAF). This method first try to set the LAF to the
|
||||
* system's LAF. If this fails, it try to use the cross platform LAF.
|
||||
* If this also fails, the LAF will not be changed.
|
||||
*/
|
||||
private void setLookAndFeel() {
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (RuntimeException t) {
|
||||
// Work-around for problems with setting Look and Feel described here:
|
||||
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468089
|
||||
Locale.setDefault(Locale.US);
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (RuntimeException t2) {
|
||||
// For some reason Ubuntu 7 can cause a NullPointerException when trying to getting the LAF
|
||||
System.err.println("Could not set the Look and Feel (LAF). The default LAF is used instead");
|
||||
}
|
||||
}
|
||||
// Java 1.6 provide system specific anti-aliasing. Enable it, if it has not been set
|
||||
if (new Double(System.getProperty("java.specification.version")) >= 1.6) {
|
||||
String aaFontSettings = System.getProperty("awt.useSystemAAFontSettings");
|
||||
|
||||
if (aaFontSettings == null) {
|
||||
System.setProperty("awt.useSystemAAFontSettings", "on");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void runIntroBattle() {
|
||||
final File intro = new File(FileUtil.getCwd(), "battles/intro.battle");
|
||||
if (intro.exists()) {
|
||||
battleManager.setBattleFilename(intro.getPath());
|
||||
battleManager.loadBattleProperties();
|
||||
|
||||
final boolean origShowResults = showResults; // save flag for showing the results
|
||||
|
||||
showResults = false;
|
||||
try {
|
||||
battleManager.startNewBattle(battleManager.loadBattleProperties(), true, false);
|
||||
battleManager.setDefaultBattleProperties();
|
||||
robocodeFrame.afterIntroBattle();
|
||||
} finally {
|
||||
showResults = origShowResults; // always restore the original flag for showing the results
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setVisibleForRobotEngine(boolean visible) {
|
||||
if (visible && !isGUIEnabled()) {
|
||||
// The GUI must be enabled in order to show the window
|
||||
setEnableGUI(true);
|
||||
|
||||
// Set the Look and Feel (LAF)
|
||||
init();
|
||||
}
|
||||
|
||||
if (isGUIEnabled()) {
|
||||
showRobocodeFrame(visible, false);
|
||||
showResults = visible;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,335 @@
|
|||
/**
|
||||
* Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://robocode.sourceforge.net/license/epl-v10.html
|
||||
*/
|
||||
package net.sf.robocode.ui.battle;
|
||||
|
||||
|
||||
import net.sf.robocode.battle.IBattleManager;
|
||||
import net.sf.robocode.battle.events.BattleEventDispatcher;
|
||||
import net.sf.robocode.battle.snapshot.RobotSnapshot;
|
||||
import net.sf.robocode.io.Logger;
|
||||
import robocode.control.events.*;
|
||||
import robocode.control.snapshot.IRobotSnapshot;
|
||||
import robocode.control.snapshot.ITurnSnapshot;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
||||
/**
|
||||
* @author Pavel Savara (original)
|
||||
*/
|
||||
public final class AwtBattleAdaptor {
|
||||
private boolean isEnabled;
|
||||
private final IBattleManager battleManager;
|
||||
private final BattleEventDispatcher battleEventDispatcher = new BattleEventDispatcher();
|
||||
private final BattleObserver observer;
|
||||
private final Timer timerTask;
|
||||
|
||||
private final AtomicReference<ITurnSnapshot> snapshot;
|
||||
private final AtomicBoolean isRunning;
|
||||
private final AtomicBoolean isPaused;
|
||||
private final AtomicInteger majorEvent;
|
||||
private final AtomicInteger lastMajorEvent;
|
||||
private ITurnSnapshot lastSnapshot;
|
||||
private StringBuilder[] outCache;
|
||||
|
||||
public AwtBattleAdaptor(IBattleManager battleManager, int maxFps, boolean skipSameFrames) {
|
||||
this.battleManager = battleManager;
|
||||
snapshot = new AtomicReference<ITurnSnapshot>(null);
|
||||
|
||||
this.skipSameFrames = skipSameFrames;
|
||||
timerTask = new Timer(1000 / maxFps, new TimerTask());
|
||||
isRunning = new AtomicBoolean(false);
|
||||
isPaused = new AtomicBoolean(false);
|
||||
majorEvent = new AtomicInteger(0);
|
||||
lastMajorEvent = new AtomicInteger(0);
|
||||
|
||||
observer = new BattleObserver();
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
timerTask.stop();
|
||||
battleManager.removeListener(observer);
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
public void subscribe(boolean isEnabled) {
|
||||
if (this.isEnabled && !isEnabled) {
|
||||
battleManager.removeListener(observer);
|
||||
timerTask.stop();
|
||||
isEnabled = false;
|
||||
} else if (!this.isEnabled && isEnabled) {
|
||||
battleManager.addListener(observer);
|
||||
isEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void addListener(IBattleListener listener) {
|
||||
battleEventDispatcher.addListener(listener);
|
||||
}
|
||||
|
||||
public synchronized void removeListener(IBattleListener listener) {
|
||||
battleEventDispatcher.removeListener(listener);
|
||||
}
|
||||
|
||||
public ITurnSnapshot getLastSnapshot() {
|
||||
return lastSnapshot;
|
||||
}
|
||||
|
||||
// this is always dispatched on AWT thread
|
||||
private void awtOnTurnEnded(boolean forceRepaint, boolean readoutText) {
|
||||
try {
|
||||
ITurnSnapshot current = snapshot.get();
|
||||
|
||||
if (current == null) { // !isRunning.get() ||
|
||||
// paint logo
|
||||
lastSnapshot = null;
|
||||
battleEventDispatcher.onTurnEnded(new TurnEndedEvent(null));
|
||||
} else {
|
||||
if (lastSnapshot != current || !skipSameFrames || forceRepaint) {
|
||||
lastSnapshot = current;
|
||||
|
||||
IRobotSnapshot[] robots = null;
|
||||
|
||||
if (readoutText) {
|
||||
synchronized (snapshot) {
|
||||
robots = lastSnapshot.getRobots();
|
||||
|
||||
for (int i = 0; i < robots.length; i++) {
|
||||
RobotSnapshot robot = (RobotSnapshot) robots[i];
|
||||
|
||||
final StringBuilder cache = outCache[i];
|
||||
|
||||
if (cache.length() > 0) {
|
||||
robot.setOutputStreamSnapshot(cache.toString());
|
||||
outCache[i].setLength(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
battleEventDispatcher.onTurnEnded(new TurnEndedEvent(lastSnapshot));
|
||||
|
||||
if (readoutText) {
|
||||
for (IRobotSnapshot robot : robots) {
|
||||
((RobotSnapshot) robot).setOutputStreamSnapshot(null);
|
||||
}
|
||||
}
|
||||
|
||||
calculateFPS();
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException t) {
|
||||
Logger.logError(t);
|
||||
}
|
||||
}
|
||||
|
||||
public int getFPS() {
|
||||
return fps;
|
||||
}
|
||||
|
||||
// FPS (frames per second) calculation
|
||||
private int fps;
|
||||
private long measuredFrameCounter;
|
||||
private long measuredFrameStartTime;
|
||||
private final boolean skipSameFrames;
|
||||
|
||||
private void calculateFPS() {
|
||||
// Calculate the current frames per second (FPS)
|
||||
|
||||
if (measuredFrameCounter++ == 0) {
|
||||
measuredFrameStartTime = System.nanoTime();
|
||||
}
|
||||
|
||||
long deltaTime = System.nanoTime() - measuredFrameStartTime;
|
||||
|
||||
if (deltaTime / 1000000000 >= 1) {
|
||||
fps = (int) (measuredFrameCounter * 1000000000L / deltaTime);
|
||||
measuredFrameCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private class TimerTask implements ActionListener {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
awtOnTurnEnded(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// BattleObserver methods are always called by battle thread
|
||||
// but everything inside invokeLater {} block in on AWT thread
|
||||
private class BattleObserver extends BattleAdaptor {
|
||||
|
||||
@Override
|
||||
public void onTurnEnded(final TurnEndedEvent event) {
|
||||
if (lastMajorEvent.get() == majorEvent.get()) {
|
||||
// snapshot is updated out of order, but always within the same major event
|
||||
snapshot.set(event.getTurnSnapshot());
|
||||
}
|
||||
|
||||
final IRobotSnapshot[] robots = event.getTurnSnapshot().getRobots();
|
||||
|
||||
for (int i = 0; i < robots.length; i++) {
|
||||
RobotSnapshot robot = (RobotSnapshot) robots[i];
|
||||
final int r = i;
|
||||
final String text = robot.getOutputStreamSnapshot();
|
||||
|
||||
if (text != null && text.length() != 0) {
|
||||
robot.setOutputStreamSnapshot(null);
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
synchronized (snapshot) {
|
||||
outCache[r].append(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isPaused.get()) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
awtOnTurnEnded(false, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRoundStarted(final RoundStartedEvent event) {
|
||||
if (lastMajorEvent.get() == majorEvent.get()) {
|
||||
snapshot.set(event.getStartSnapshot());
|
||||
}
|
||||
majorEvent.incrementAndGet();
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
awtOnTurnEnded(true, false);
|
||||
battleEventDispatcher.onRoundStarted(event);
|
||||
lastMajorEvent.incrementAndGet();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBattleStarted(final BattleStartedEvent event) {
|
||||
majorEvent.incrementAndGet();
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
isRunning.set(true);
|
||||
isPaused.set(false);
|
||||
synchronized (snapshot) {
|
||||
outCache = new StringBuilder[event.getRobotsCount()];
|
||||
for (int i = 0; i < event.getRobotsCount(); i++) {
|
||||
outCache[i] = new StringBuilder(1024);
|
||||
}
|
||||
}
|
||||
snapshot.set(null);
|
||||
battleEventDispatcher.onBattleStarted(event);
|
||||
lastMajorEvent.incrementAndGet();
|
||||
awtOnTurnEnded(true, false);
|
||||
timerTask.start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBattleFinished(final BattleFinishedEvent event) {
|
||||
majorEvent.incrementAndGet();
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
isRunning.set(false);
|
||||
isPaused.set(false);
|
||||
timerTask.stop();
|
||||
// flush text cache
|
||||
awtOnTurnEnded(true, true);
|
||||
|
||||
battleEventDispatcher.onBattleFinished(event);
|
||||
lastMajorEvent.incrementAndGet();
|
||||
snapshot.set(null);
|
||||
|
||||
// paint logo
|
||||
awtOnTurnEnded(true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBattleCompleted(final BattleCompletedEvent event) {
|
||||
majorEvent.incrementAndGet();
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
battleEventDispatcher.onBattleCompleted(event);
|
||||
lastMajorEvent.incrementAndGet();
|
||||
awtOnTurnEnded(true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRoundEnded(final RoundEndedEvent event) {
|
||||
majorEvent.incrementAndGet();
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
battleEventDispatcher.onRoundEnded(event);
|
||||
lastMajorEvent.incrementAndGet();
|
||||
awtOnTurnEnded(true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBattlePaused(final BattlePausedEvent event) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
timerTask.stop();
|
||||
battleEventDispatcher.onBattlePaused(event);
|
||||
awtOnTurnEnded(true, true);
|
||||
isPaused.set(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBattleResumed(final BattleResumedEvent event) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
battleEventDispatcher.onBattleResumed(event);
|
||||
if (isRunning.get()) {
|
||||
timerTask.start();
|
||||
isPaused.set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBattleMessage(final BattleMessageEvent event) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
battleEventDispatcher.onBattleMessage(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBattleError(final BattleErrorEvent event) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
battleEventDispatcher.onBattleError(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue