diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.api/.markers b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.api/.markers new file mode 100644 index 0000000..cf728b1 Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.api/.markers differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.battle/.markers b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.battle/.markers new file mode 100644 index 0000000..b8400f5 Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.battle/.markers differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.core/.markers b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.core/.markers new file mode 100644 index 0000000..5a5ffb3 Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.core/.markers differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.host/.markers b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.host/.markers new file mode 100644 index 0000000..9af9502 Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.host/.markers differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.roborumble/.markers b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.roborumble/.markers new file mode 100644 index 0000000..bd20155 Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.roborumble/.markers differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.ui.editor/.markers b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.ui.editor/.markers new file mode 100644 index 0000000..e8c6943 Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.ui.editor/.markers differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.ui/.markers b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.ui/.markers new file mode 100644 index 0000000..f4be5ab Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.projects/robocode.ui/.markers differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources new file mode 100644 index 0000000..4683b6c Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.launching.prefs b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.launching.prefs new file mode 100644 index 0000000..4a0e534 --- /dev/null +++ b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.launching.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.launching.PREF_VM_XML=\r\n\r\n\r\n\r\n\r\n\r\n diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.rse.core.prefs b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.rse.core.prefs new file mode 100644 index 0000000..0a60e67 --- /dev/null +++ b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.rse.core.prefs @@ -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; diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi new file mode 100644 index 0000000..8c2319f --- /dev/null +++ b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi @@ -0,0 +1,2502 @@ + + + + activeSchemeId:org.eclipse.ui.defaultAcceleratorConfiguration + + + + + + topLevel + shellMaximized + + + Minimized + MinimizedByZoom + + + persp.actionSet:org.eclipse.mylyn.context.ui.actionSet + persp.actionSet:org.eclipse.mylyn.doc.actionSet + persp.actionSet:org.eclipse.mylyn.tasks.ui.navigation + persp.actionSet:org.eclipse.ui.cheatsheets.actionSet + persp.actionSet:org.eclipse.rse.core.search.searchActionSet + persp.actionSet:org.eclipse.search.searchActionSet + persp.actionSet:org.eclipse.ui.edit.text.actionSet.annotationNavigation + persp.actionSet:org.eclipse.ui.edit.text.actionSet.navigation + persp.actionSet:org.eclipse.ui.edit.text.actionSet.convertLineDelimitersTo + persp.actionSet:org.eclipse.ui.externaltools.ExternalToolsSet + persp.actionSet:org.eclipse.ui.actionSet.keyBindings + persp.actionSet:org.eclipse.ui.actionSet.openFiles + persp.actionSet:org.eclipse.jst.j2ee.J2eeMainActionSet + persp.actionSet:org.eclipse.jdt.ui.JavaActionSet + persp.actionSet:org.eclipse.debug.ui.launchActionSet + persp.actionSet:org.eclipse.debug.ui.debugActionSet + persp.actionSet:org.eclipse.ui.NavigateActionSet + persp.viewSC:org.eclipse.ui.navigator.ProjectExplorer + persp.viewSC:org.eclipse.wst.server.ui.ServersView + persp.viewSC:org.eclipse.datatools.connectivity.DataSourceExplorerNavigator + persp.viewSC:org.eclipse.ui.views.BookmarkView + persp.viewSC:org.eclipse.ui.views.ContentOutline + persp.viewSC:org.eclipse.ui.views.PropertySheet + persp.viewSC:org.eclipse.ui.views.ResourceNavigator + persp.viewSC:org.eclipse.wst.common.snippets.internal.ui.SnippetsView + persp.viewSC:org.eclipse.ui.views.AllMarkersView + persp.viewSC:org.eclipse.mylyn.tasks.ui.views.tasks + persp.viewSC:org.eclipse.search.ui.views.SearchView + persp.viewSC:org.eclipse.ui.console.ConsoleView + persp.showIn:org.eclipse.ui.navigator.ProjectExplorer + persp.newWizSC:org.eclipse.jpt.jpa.ui.wizard.newJpaProject + persp.perspSC:org.eclipse.jpt.ui.jpaPerspective + persp.perspSC:org.eclipse.debug.ui.DebugPerspective + persp.perspSC:org.eclipse.jdt.ui.JavaPerspective + persp.perspSC:org.eclipse.ui.resourcePerspective + persp.perspSC:org.eclipse.wst.web.ui.webDevPerspective + persp.newWizSC:org.eclipse.jst.j2ee.ui.project.facet.EarProjectWizard + persp.newWizSC:org.eclipse.jst.servlet.ui.project.facet.WebProjectWizard + persp.newWizSC:org.eclipse.jst.ejb.ui.project.facet.EjbProjectWizard + persp.newWizSC:org.eclipse.jst.j2ee.jca.ui.internal.wizard.ConnectorProjectWizard + persp.newWizSC:org.eclipse.jst.j2ee.ui.project.facet.appclient.AppClientProjectWizard + persp.newWizSC:org.eclipse.wst.web.ui.internal.wizards.SimpleWebProjectWizard + persp.newWizSC:org.eclipse.jpt.ui.wizard.newJpaProject + persp.newWizSC:org.eclipse.jst.servlet.ui.internal.wizard.AddServletWizard + persp.newWizSC:org.eclipse.jst.ejb.ui.internal.wizard.AddSessionBeanWizard + persp.newWizSC:org.eclipse.jst.ejb.ui.internal.wizard.AddMessageDrivenBeanWizard + persp.newWizSC:org.eclipse.jpt.ui.wizard.newEntity + persp.newWizSC:org.eclipse.jst.ws.creation.ui.wizard.serverwizard + persp.newWizSC:org.eclipse.ui.wizards.new.folder + persp.newWizSC:org.eclipse.ui.wizards.new.file + persp.actionSet:org.eclipse.wst.server.ui.internal.webbrowser.actionSet + persp.actionSet:org.eclipse.debug.ui.breakpointActionSet + persp.newWizSC:org.eclipse.m2e.core.wizards.Maven2ProjectWizard + persp.actionSet:org.eclipse.wst.ws.explorer.explorer + + + newtablook + + + + + + + + + + newtablook + + + + + + newtablook + + + + + + + + + + + + + + + + + + Maximized + + + + + + + View + categoryTag:Help + + + + View + categoryTag:General + activeOnClose + + ViewMenu + menuContribution:menu + + + + + View + categoryTag:Help + + + + newtablook + org.eclipse.e4.primaryDataStack + EditorStack + + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + View + categoryTag:General + + + View + categoryTag:Java + + + View + categoryTag:Java Browsing + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + View + categoryTag:General + + + View + categoryTag:Server + + + View + categoryTag:Data Management + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + View + categoryTag:Mylyn + + + + toolbarSeparator + + + + Draggable + + + + + + + + + + + + + + + toolbarSeparator + + + + Draggable + + + + + Draggable + + + + + + + + + + + + + + + + + + + Draggable + + + + + + + Draggable + + + + + + Draggable + + + + + + + Draggable + + + + + Draggable + + + + + toolbarSeparator + + + + Draggable + + + + + + + + + + + + toolbarSeparator + + + + toolbarSeparator + + + + Draggable + + + + + stretch + + + glue + + + + glue + + + Draggable + + + + + stretch + + + + Draggable + + + + + TrimStack + + + + + + + + + + + + + + + + + + + + + + + + + locale:zh + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + locale:zh + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + locale:zh + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + locale:zh + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + platform:win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Editor + + + View + categoryTag:Ant + + + View + categoryTag:Data Management + + + View + categoryTag:Data Management + + + View + categoryTag:Data Management + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Git + + + View + categoryTag:Git + + + View + categoryTag:Git + + + View + categoryTag:Git + + + View + categoryTag:General + + + View + categoryTag:Help + + + View + categoryTag:Debug + + + View + categoryTag:Java + + + View + categoryTag:Java + + + View + categoryTag:Java + + + View + categoryTag:Java Browsing + + + View + categoryTag:Java Browsing + + + View + categoryTag:Java Browsing + + + View + categoryTag:Java Browsing + + + View + categoryTag:Java + + + View + categoryTag:General + + + View + categoryTag:Java + + + View + categoryTag:Java + + + View + categoryTag:JPA + + + View + categoryTag:JPA + + + View + categoryTag:JavaServer Faces + + + View + categoryTag:JavaServer Faces + + + View + categoryTag:Web Services + + + View + categoryTag:Maven + + + View + categoryTag:Maven + + + View + categoryTag:Mylyn + + + View + categoryTag:Mylyn + + + View + categoryTag:Mylyn + + + View + categoryTag:API Tools + + + View + categoryTag:Plug-in Development + + + View + categoryTag:Plug-in Development + + + View + categoryTag:Plug-in Development + + + View + categoryTag:Plug-in Development + + + View + categoryTag:Plug-in Development + + + View + categoryTag:Remote Systems + + + View + categoryTag:Remote Systems + + + View + categoryTag:Remote Systems + + + View + categoryTag:Remote Systems + + + View + categoryTag:Remote Systems + + + View + categoryTag:Remote Systems + + + View + categoryTag:Remote Systems + + + View + categoryTag:Remote Systems + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:CVS + + + View + categoryTag:CVS + + + View + categoryTag:Team + + + View + categoryTag:Team + + + View + categoryTag:Terminal + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:Help + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:Debug + + + View + categoryTag:JavaScript + + + View + categoryTag:JavaScript + + + View + categoryTag:JavaScript + + + View + categoryTag:JavaScript + + + View + categoryTag:JavaScript + + + View + categoryTag:Server + + + View + categoryTag:XML + + + View + categoryTag:XML + + + View + categoryTag:XML + + + View + categoryTag:XML + + + View + categoryTag:XML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps new file mode 100644 index 0000000..f566629 Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt new file mode 100644 index 0000000..f84b816 --- /dev/null +++ b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt @@ -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 diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat new file mode 100644 index 0000000..0facbcd Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.m2e.core/nexus/1a42e2ddbdd28c4e903ebde7bafa5867/segments.gen b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.m2e.core/nexus/1a42e2ddbdd28c4e903ebde7bafa5867/segments.gen new file mode 100644 index 0000000..e9fa600 Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.m2e.core/nexus/1a42e2ddbdd28c4e903ebde7bafa5867/segments.gen differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.m2e.core/nexus/26522e0d83a422eed93329ece7565cfc/segments.gen b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.m2e.core/nexus/26522e0d83a422eed93329ece7565cfc/segments.gen new file mode 100644 index 0000000..e9fa600 Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.m2e.core/nexus/26522e0d83a422eed93329ece7565cfc/segments.gen differ diff --git a/代码/workspace_robo4/.metadata/.plugins/org.eclipse.m2e.core/nexus/830bc118332e77292949ed1e6d2fabe0/segments.gen b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.m2e.core/nexus/830bc118332e77292949ed1e6d2fabe0/segments.gen new file mode 100644 index 0000000..e9fa600 Binary files /dev/null and b/代码/workspace_robo4/.metadata/.plugins/org.eclipse.m2e.core/nexus/830bc118332e77292949ed1e6d2fabe0/segments.gen differ diff --git a/代码/workspace_robo4/plugins/dotnet/robocode.dotnet.host/src/main/java/net/sf/robocode/dotnet/host/Module.java b/代码/workspace_robo4/plugins/dotnet/robocode.dotnet.host/src/main/java/net/sf/robocode/dotnet/host/Module.java new file mode 100644 index 0000000..e538d7a --- /dev/null +++ b/代码/workspace_robo4/plugins/dotnet/robocode.dotnet.host/src/main/java/net/sf/robocode/dotnet/host/Module.java @@ -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 allModules) { + net.sf.robocode.dotnet.nhost.ModuleN.InitN(); + } +} diff --git a/代码/workspace_robo4/plugins/dotnet/robocode.dotnet.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java b/代码/workspace_robo4/plugins/dotnet/robocode.dotnet.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java new file mode 100644 index 0000000..852475f --- /dev/null +++ b/代码/workspace_robo4/plugins/dotnet/robocode.dotnet.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java @@ -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; + } +} diff --git a/代码/workspace_robo4/plugins/dotnet/robocode.dotnet.tests/src/test/java/net/sf/robocode/test/robotscs/TestMaxTurnRate.java b/代码/workspace_robo4/plugins/dotnet/robocode.dotnet.tests/src/test/java/net/sf/robocode/test/robotscs/TestMaxTurnRate.java new file mode 100644 index 0000000..5507bb1 --- /dev/null +++ b/代码/workspace_robo4/plugins/dotnet/robocode.dotnet.tests/src/test/java/net/sf/robocode/test/robotscs/TestMaxTurnRate.java @@ -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")); + } + } +} diff --git a/代码/workspace_robo4/plugins/testing/robocode.testing.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java b/代码/workspace_robo4/plugins/testing/robocode.testing.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java new file mode 100644 index 0000000..e993cf8 --- /dev/null +++ b/代码/workspace_robo4/plugins/testing/robocode.testing.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java @@ -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; + } +} diff --git a/代码/workspace_robo4/robocode.api/src/main/java/net/sf/robocode/security/HiddenAccess.java b/代码/workspace_robo4/robocode.api/src/main/java/net/sf/robocode/security/HiddenAccess.java new file mode 100644 index 0000000..184001c --- /dev/null +++ b/代码/workspace_robo4/robocode.api/src/main/java/net/sf/robocode/security/HiddenAccess.java @@ -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 urls = new ArrayList(); + + 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); + } + } + +} diff --git a/代码/workspace_robo4/robocode.core/src/main/java/net/sf/robocode/battle/events/BattleEventDispatcher.java b/代码/workspace_robo4/robocode.core/src/main/java/net/sf/robocode/battle/events/BattleEventDispatcher.java new file mode 100644 index 0000000..9155e67 --- /dev/null +++ b/代码/workspace_robo4/robocode.core/src/main/java/net/sf/robocode/battle/events/BattleEventDispatcher.java @@ -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 listeners = new CopyOnWriteArrayList(); + + 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()); + } + } + } +} diff --git a/代码/workspace_robo4/robocode.core/src/main/java/net/sf/robocode/core/RobocodeMain.java b/代码/workspace_robo4/robocode.core/src/main/java/net/sf/robocode/core/RobocodeMain.java new file mode 100644 index 0000000..3ba5a93 --- /dev/null +++ b/代码/workspace_robo4/robocode.core/src/main/java/net/sf/robocode/core/RobocodeMain.java @@ -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.
+ * Copyright (c) 2001-2016 Mathew A. Nelson and Robocode contributors + * + * @see robocode.sourceforge.net + * + * @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 Change the current working directory\n" + + " -battle Run the battle specified in a battle file\n" + + " -results Save results to the specified text file\n" + + " -record Record the battle into the specified file as binary\n" + + " -recordXML Record the battle into the specified file as XML\n" + + " -replay Replay the specified battle record\n" + + " -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= Set the working directory\n" + + " -DROBOTPATH= Set the robots directory (default is 'robots')\n" + + " -DBATTLEPATH= 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= 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()); + } +} diff --git a/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/JavaHost.java b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/JavaHost.java new file mode 100644 index 0000000..7dde1e9 --- /dev/null +++ b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/JavaHost.java @@ -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; + } + +} diff --git a/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/events/EventManager.java b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/events/EventManager.java new file mode 100644 index 0000000..5621c87 --- /dev/null +++ b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/events/EventManager.java @@ -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 customEvents = new CopyOnWriteArrayList(); + private final EventQueue eventQueue; + + private final boolean[] interruptible = new boolean[MAX_PRIORITY + 1]; + private Event currentTopEvent; + private int currentTopEventPriority; + private ScannedRobotEvent dummyScannedRobotEvent; + private Map 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. + *

+ * 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 getAllEvents() { + List events = new ArrayList(); + 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 getBulletHitBulletEvents() { + List events = new ArrayList(); + 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 getBulletHitEvents() { + List events = new ArrayList(); + 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 getBulletMissedEvents() { + List events = new ArrayList(); + 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 getHitByBulletEvents() { + List events = new ArrayList(); + 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 getHitRobotEvents() { + List events = new ArrayList(); + + 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 getHitWallEvents() { + List events = new ArrayList(); + 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 getRobotDeathEvents() { + List events = new ArrayList(); + 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 getScannedRobotEvents() { + List events = new ArrayList(); + 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 getMessageEvents() { + List events = new ArrayList(); + 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 getStatusEvents() { + List events = new ArrayList(); + 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. + *

+ * 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(); + 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); + } + } +} diff --git a/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/proxies/HostingRobotProxy.java b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/proxies/HostingRobotProxy.java new file mode 100644 index 0000000..9fdb31e --- /dev/null +++ b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/proxies/HostingRobotProxy.java @@ -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 securityViolations = Collections.synchronizedSet(new HashSet()); + + 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); + } + } + } + } +} diff --git a/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/security/RobotClassLoader.java b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/security/RobotClassLoader.java new file mode 100644 index 0000000..0993839 --- /dev/null +++ b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/security/RobotClassLoader.java @@ -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 referencedClasses = new HashSet(); + + // Cached names on found system classes + private Set foundSystemClasses = new HashSet(); + + // 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() { + 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 clone; + + do { + clone = new HashSet(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(), type)) { + if (isStaticReference(field)) { + cleanStaticReference(field); + } + } + } + } catch (RuntimeException t) { + Logger.logError(t); + } + } + + private void warnIfStaticRobotInstanceFields() { + if (staticRobotInstanceWarning == null) { + List staticRobotReferences = new ArrayList(); + + 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(), 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 getAllFields(List 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()); + } +} diff --git a/代码/workspace_robo4/robocode.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java b/代码/workspace_robo4/robocode.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java new file mode 100644 index 0000000..e4c6d0a --- /dev/null +++ b/代码/workspace_robo4/robocode.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java @@ -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; + } +} diff --git a/代码/workspace_robo4/robocode.roborumble/src/main/java/net/sf/robocode/roborumble/netengine/FileTransfer.java b/代码/workspace_robo4/robocode.roborumble/src/main/java/net/sf/robocode/roborumble/netengine/FileTransfer.java new file mode 100644 index 0000000..09004c4 --- /dev/null +++ b/代码/workspace_robo4/robocode.roborumble/src/main/java/net/sf/robocode/roborumble/netengine/FileTransfer.java @@ -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) {} + } + } +} diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/AwtAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/AwtAttack.java new file mode 100644 index 0000000..5722779 --- /dev/null +++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/AwtAttack.java @@ -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); + } + } + +} diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorAwtAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorAwtAttack.java new file mode 100644 index 0000000..47ae244 --- /dev/null +++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorAwtAttack.java @@ -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); + } + } +} diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorThreadAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorThreadAttack.java new file mode 100644 index 0000000..0c5a0f8 --- /dev/null +++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorThreadAttack.java @@ -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) {} + } + } +} diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/FileAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/FileAttack.java new file mode 100644 index 0000000..9f76ccf --- /dev/null +++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/FileAttack.java @@ -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); + } + } + +} diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/IncludeNamespaceAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/IncludeNamespaceAttack.java new file mode 100644 index 0000000..c5ecdb9 --- /dev/null +++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/IncludeNamespaceAttack.java @@ -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); + } + } +} diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadAttack.java new file mode 100644 index 0000000..bb21f8c --- /dev/null +++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadAttack.java @@ -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); + } + } + } +} diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadGroupAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadGroupAttack.java new file mode 100644 index 0000000..963b549 --- /dev/null +++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadGroupAttack.java @@ -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); + } + } + } +} diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/UndeadThread.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/UndeadThread.java new file mode 100644 index 0000000..75fcbfa --- /dev/null +++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/UndeadThread.java @@ -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); + } + } + +} diff --git a/代码/workspace_robo4/robocode.tests/src/main/java/net/sf/robocode/test/helpers/Assert.java b/代码/workspace_robo4/robocode.tests/src/main/java/net/sf/robocode/test/helpers/Assert.java new file mode 100644 index 0000000..27c29dd --- /dev/null +++ b/代码/workspace_robo4/robocode.tests/src/main/java/net/sf/robocode/test/helpers/Assert.java @@ -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 void allAssertThat(T t, org.hamcrest.Matcher tMatcher) { + try { + org.junit.Assert.assertThat(t, tMatcher); + } catch (RuntimeException ex) { + ex.printStackTrace(System.err); + } + } + +} diff --git a/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/EditWindow.java b/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/EditWindow.java new file mode 100644 index 0000000..a0f8e9b --- /dev/null +++ b/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/EditWindow.java @@ -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; + } +} diff --git a/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/WindowMenuItem.java b/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/WindowMenuItem.java new file mode 100644 index 0000000..bda1627 --- /dev/null +++ b/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/WindowMenuItem.java @@ -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 + *

+ * Brings the window to the front. This should be called for the "More + * Windows..." Item, because it doesn't make itself its own ActionListener. + *

+ * 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. + *

+ * @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. + *

+ * 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). + *

+ * 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". + *

+ * 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. + *

+ * Handy for repurposing the menu items as list items :-) + */ + @Override + public String toString() { + return (type == SPECIAL_MORE) ? "" : getFileName(); + } +} diff --git a/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/WindowManager.java b/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/WindowManager.java new file mode 100644 index 0000000..bbdae95 --- /dev/null +++ b/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/WindowManager.java @@ -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; + } + } +} diff --git a/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/battle/AwtBattleAdaptor.java b/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/battle/AwtBattleAdaptor.java new file mode 100644 index 0000000..11436ae --- /dev/null +++ b/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/battle/AwtBattleAdaptor.java @@ -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 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(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); + } + }); + } + } +}