diff --git a/.gitignore b/.gitignore index 68f6451a..e0667880 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ hyperlap2d-runtime-libgdx/build/ plugin-9patch/build/ plugin-performance/build/ plugin-tiled/build/ +plugin-skin-composer/build/ assets/configs/config.pit assets/tiled.atlas assets/tiled.png diff --git a/assets/cache/SkinComposer.jar b/assets/cache/SkinComposer.jar new file mode 100644 index 00000000..24020f6e Binary files /dev/null and b/assets/cache/SkinComposer.jar differ diff --git a/assets/plugins/plugin-skin-composer-0.0.1.jar b/assets/plugins/plugin-skin-composer-0.0.1.jar new file mode 100644 index 00000000..35a41bf3 Binary files /dev/null and b/assets/plugins/plugin-skin-composer-0.0.1.jar differ diff --git a/src/main/java/games/rednblack/editor/view/ui/widget/ProgressHandler.java b/hyperlap2d-common-api/src/main/java/games/rednblack/h2d/common/ProgressHandler.java similarity index 95% rename from src/main/java/games/rednblack/editor/view/ui/widget/ProgressHandler.java rename to hyperlap2d-common-api/src/main/java/games/rednblack/h2d/common/ProgressHandler.java index e0409619..34c60910 100644 --- a/src/main/java/games/rednblack/editor/view/ui/widget/ProgressHandler.java +++ b/hyperlap2d-common-api/src/main/java/games/rednblack/h2d/common/ProgressHandler.java @@ -16,7 +16,7 @@ * ***************************************************************************** */ -package games.rednblack.editor.view.ui.widget; +package games.rednblack.h2d.common; public interface ProgressHandler { diff --git a/hyperlap2d-common-api/src/main/java/games/rednblack/h2d/common/plugins/PluginAPI.java b/hyperlap2d-common-api/src/main/java/games/rednblack/h2d/common/plugins/PluginAPI.java index 4b73009e..caf8a24b 100644 --- a/hyperlap2d-common-api/src/main/java/games/rednblack/h2d/common/plugins/PluginAPI.java +++ b/hyperlap2d-common-api/src/main/java/games/rednblack/h2d/common/plugins/PluginAPI.java @@ -68,6 +68,11 @@ public interface PluginAPI { */ public String getPluginDir(); + /** + * @return Path of cache directory + */ + public String getCacheDir(); + /** * @return Path of working project */ diff --git a/plugin-performance/src/main/java/games/rednblack/editor/plugin/performance/PerformancePlugin.java b/plugin-performance/src/main/java/games/rednblack/editor/plugin/performance/PerformancePlugin.java index a73561c6..d2138502 100644 --- a/plugin-performance/src/main/java/games/rednblack/editor/plugin/performance/PerformancePlugin.java +++ b/plugin-performance/src/main/java/games/rednblack/editor/plugin/performance/PerformancePlugin.java @@ -10,7 +10,7 @@ public class PerformancePlugin extends H2DPluginAdapter { public static final String PANEL_OPEN = CLASS_NAME + ".PANEL_OPEN"; public static final String WINDOWS_MENU = "games.rednblack.editor.view.HyperLap2DMenuBar.WINDOW_MENU"; - private PerformancePanelMediator performancePanelMediator; + private final PerformancePanelMediator performancePanelMediator; public PerformancePlugin() { performancePanelMediator = new PerformancePanelMediator(this); diff --git a/plugin-skin-composer/build.gradle b/plugin-skin-composer/build.gradle new file mode 100644 index 00000000..442ac9a2 --- /dev/null +++ b/plugin-skin-composer/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'java' +} + +group 'games.rednblack' +version '0.0.1' + +repositories { + mavenCentral() + maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } +} + +ext { + gdxVersion = '1.9.10' + box2DLightsVersion = '1.5' + ashleyVersion = '1.7.3' + visUIVersion = '1.4.5-SNAPSHOT' +} + +dependencies { + implementation "com.badlogicgames.gdx:gdx:$gdxVersion" + implementation "com.badlogicgames.ashley:ashley:$ashleyVersion" + implementation "com.kotcrab.vis:vis-ui:$visUIVersion" + + implementation 'net.mountainblade:modular:1.0' + + implementation project(":hyperlap2d-common-api") + implementation project(":hyperlap2d-runtime-libgdx") + + implementation "org.apache.commons:commons-io:1.3.2" + + testCompile group: 'junit', name: 'junit', version: '4.12' +} + +task dist(type: Jar) { + from files(sourceSets.main.output.classesDirs) + from files(sourceSets.main.output.resourcesDir) +} \ No newline at end of file diff --git a/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/DownloadingDialog.java b/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/DownloadingDialog.java new file mode 100644 index 00000000..905c2dcd --- /dev/null +++ b/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/DownloadingDialog.java @@ -0,0 +1,59 @@ +package games.rednblack.editor.plugin.skincomposer; + +import com.kotcrab.vis.ui.widget.VisLabel; +import com.kotcrab.vis.ui.widget.VisProgressBar; +import com.kotcrab.vis.ui.widget.VisTable; +import games.rednblack.h2d.common.ProgressHandler; +import games.rednblack.h2d.common.UIDraggablePanel; + +public class DownloadingDialog extends UIDraggablePanel implements ProgressHandler { + private VisLabel downloadingLabel; + private VisProgressBar progressBar; + + public DownloadingDialog () { + super("Skin Composer Plugin"); + setMovable(false); + setModal(false); + + setHeight(100); + setWidth(250); + + VisTable mainTable = new VisTable(); + add(mainTable).fill().expand(); + + downloadingLabel = new VisLabel("Downloading info ..."); + mainTable.add(downloadingLabel).left(); + mainTable.row().padBottom(5); + + progressBar = new VisProgressBar(0, 100, 1, false); + mainTable.add(progressBar).fillX().pad(5).width(240); + mainTable.row().padBottom(5); + + pack(); + } + + public void setCurrentVersion(String name) { + downloadingLabel.setText("Downloading "+ name + " ..."); + } + + @Override + public void progressStarted() { + + } + + @Override + public void progressChanged(float value) { + progressBar.setValue(value); + } + + @Override + public void progressComplete() { + close(); + } + + @Override + public void progressFailed() { + downloadingLabel.setText("Download failed!"); + addCloseButton(); + } +} diff --git a/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/GithubReleaseData.java b/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/GithubReleaseData.java new file mode 100644 index 00000000..baec1313 --- /dev/null +++ b/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/GithubReleaseData.java @@ -0,0 +1,14 @@ +package games.rednblack.editor.plugin.skincomposer; + +import com.badlogic.gdx.utils.Array; + +public class GithubReleaseData { + public String tag_name; + public String name; + public Array assets; + + public static class GithubReleaseAssetData { + String name; + String browser_download_url; + } +} diff --git a/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/HttpDownloadUtility.java b/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/HttpDownloadUtility.java new file mode 100644 index 00000000..0f41d1a0 --- /dev/null +++ b/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/HttpDownloadUtility.java @@ -0,0 +1,118 @@ +package games.rednblack.editor.plugin.skincomposer; + +import games.rednblack.h2d.common.ProgressHandler; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * A utility that downloads a file from a URL. + * @author www.codejava.net + * + */ +public class HttpDownloadUtility { + private static final int BUFFER_SIZE = 4096; + + /** + * Downloads a file from a URL + * @param fileURL HTTP URL of the file to be downloaded + * @param saveDir path of the directory to save the file + * @throws IOException + */ + public static void downloadFile(String fileURL, String saveDir, ProgressHandler progressHandler) + throws IOException { + URL url = new URL(fileURL); + HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); + httpConn.setInstanceFollowRedirects(true); + int responseCode = httpConn.getResponseCode(); + + // always check HTTP response code first + if (responseCode == HttpURLConnection.HTTP_OK) { + String fileName = ""; + String disposition = httpConn.getHeaderField("Content-Disposition"); + String contentType = httpConn.getContentType(); + int contentLength = httpConn.getContentLength(); + + if (disposition != null) { + // extracts file name from header field + int index = disposition.indexOf("filename="); + if (index > 0) { + fileName = disposition.substring(index + 9); + } + } else { + // extracts file name from URL + fileName = fileURL.substring(fileURL.lastIndexOf("/") + 1); + } + + System.out.println("Content-Type = " + contentType); + System.out.println("Content-Disposition = " + disposition); + System.out.println("Content-Length = " + contentLength); + System.out.println("fileName = " + fileName); + + // opens input stream from the HTTP connection + InputStream inputStream = httpConn.getInputStream(); + String saveFilePath = saveDir + File.separator + fileName; + + // opens an output stream to save into file + FileOutputStream outputStream = new FileOutputStream(saveFilePath); + int currentBytes = 0; + + int bytesRead = -1; + byte[] buffer = new byte[BUFFER_SIZE]; + while ((bytesRead = inputStream.read(buffer)) != -1) { + currentBytes += bytesRead; + progressHandler.progressChanged(currentBytes * 100f / contentLength); + outputStream.write(buffer, 0, bytesRead); + } + + outputStream.close(); + inputStream.close(); + + progressHandler.progressComplete(); + } else { + System.out.println("No file to download. Server replied HTTP code: " + responseCode); + progressHandler.progressFailed(); + } + + httpConn.disconnect(); + } + + public static String downloadToString(String fileURL) throws IOException { + URL url = new URL(fileURL); + HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); + httpConn.setInstanceFollowRedirects(true); + int responseCode = httpConn.getResponseCode(); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + // always check HTTP response code first + if (responseCode == HttpURLConnection.HTTP_OK) { + String disposition = httpConn.getHeaderField("Content-Disposition"); + String contentType = httpConn.getContentType(); + int contentLength = httpConn.getContentLength(); + + + System.out.println("Content-Type = " + contentType); + System.out.println("Content-Disposition = " + disposition); + System.out.println("Content-Length = " + contentLength); + + // opens input stream from the HTTP connection + InputStream inputStream = httpConn.getInputStream(); + + int bytesRead = -1; + byte[] buffer = new byte[BUFFER_SIZE]; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + outputStream.close(); + inputStream.close(); + } else { + System.out.println("No file to download. Server replied HTTP code: " + responseCode); + } + httpConn.disconnect(); + + return outputStream.toString("UTF-8"); + } +} diff --git a/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/SkinComposerMediator.java b/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/SkinComposerMediator.java new file mode 100644 index 00000000..257302f0 --- /dev/null +++ b/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/SkinComposerMediator.java @@ -0,0 +1,90 @@ +package games.rednblack.editor.plugin.skincomposer; + +import com.badlogic.gdx.utils.Json; +import com.puremvc.patterns.mediator.SimpleMediator; +import com.puremvc.patterns.observer.Notification; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class SkinComposerMediator extends SimpleMediator { + private static final String TAG = SkinComposerMediator.class.getCanonicalName(); + public static final String NAME = TAG; + + private final SkinComposerPlugin plugin; + + public SkinComposerMediator(SkinComposerPlugin plugin) { + super(NAME, new DownloadingDialog()); + this.plugin = plugin; + } + + @Override + public String[] listNotificationInterests() { + return new String[]{ + SkinComposerPlugin.PANEL_OPEN + }; + } + + + @Override + public void handleNotification(Notification notification) { + super.handleNotification(notification); + + switch (notification.getName()) { + case SkinComposerPlugin.PANEL_OPEN: + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(() -> { + String pluginPath = plugin.getAPI().getCacheDir(); + String jarPath = pluginPath + File.separator + "SkinComposer.jar"; + if (new File(jarPath).exists()) { + try { + Runtime.getRuntime().exec(" java -jar " + jarPath); + } catch (IOException e) { + e.printStackTrace(); + } + return; + } + + viewComponent.show(plugin.getAPI().getUIStage()); + + try { + FileUtils.forceMkdir(new File(pluginPath)); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + String data = HttpDownloadUtility.downloadToString("https://api.github.com/repos/raeleus/skin-composer/releases/latest"); + Json json = new Json(); + json.setIgnoreUnknownFields(true); + GithubReleaseData jsonData = json.fromJson(GithubReleaseData.class, data); + viewComponent.setCurrentVersion(jsonData.name); + for (GithubReleaseData.GithubReleaseAssetData assetData : jsonData.assets) { + if (assetData.name.equals("SkinComposer.jar")) { + try { + HttpDownloadUtility.downloadFile(assetData.browser_download_url, pluginPath, viewComponent); + } catch (IOException e) { + viewComponent.progressFailed(); + e.printStackTrace(); + } + break; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + + try { + Runtime.getRuntime().exec(" java -jar " + jarPath); + } catch (IOException e) { + e.printStackTrace(); + } + }); + executor.shutdown(); + break; + } + } +} diff --git a/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/SkinComposerPlugin.java b/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/SkinComposerPlugin.java new file mode 100644 index 00000000..72c31e80 --- /dev/null +++ b/plugin-skin-composer/src/main/java/games/rednblack/editor/plugin/skincomposer/SkinComposerPlugin.java @@ -0,0 +1,24 @@ +package games.rednblack.editor.plugin.skincomposer; + +import games.rednblack.h2d.common.plugins.H2DPluginAdapter; +import net.mountainblade.modular.annotations.Implementation; + +@Implementation(authors = "fgnm", version = "0.0.1") +public class SkinComposerPlugin extends H2DPluginAdapter { + public static final String CLASS_NAME = "games.rednblack.editor.plugin.skincomposer"; + + public static final String PANEL_OPEN = CLASS_NAME + ".PANEL_OPEN"; + public static final String WINDOWS_MENU = "games.rednblack.editor.view.HyperLap2DMenuBar.WINDOW_MENU"; + + private final SkinComposerMediator skinComposerMediator; + + public SkinComposerPlugin() { + skinComposerMediator = new SkinComposerMediator(this); + } + + @Override + public void initPlugin() { + facade.registerMediator(skinComposerMediator); + pluginAPI.addMenuItem(WINDOWS_MENU, "Skin Composer", PANEL_OPEN); + } +} diff --git a/settings.gradle b/settings.gradle index e9c20eac..28e4d329 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,4 +5,5 @@ include 'hyperlap2d-common-api' include 'plugin-performance' include 'plugin-9patch' include 'plugin-tiled' +include 'plugin-skin-composer' diff --git a/src/main/java/games/rednblack/editor/controller/BootstrapPlugins.java b/src/main/java/games/rednblack/editor/controller/BootstrapPlugins.java index 8dc36ac7..a1320817 100644 --- a/src/main/java/games/rednblack/editor/controller/BootstrapPlugins.java +++ b/src/main/java/games/rednblack/editor/controller/BootstrapPlugins.java @@ -48,11 +48,13 @@ public class BootstrapPlugins extends SimpleCommand { ProjectManager projectManager = facade.retrieveProxy(ProjectManager.NAME); File pluginDir = new File(projectManager.getRootPath() + File.separator + "plugins"); + File cacheDir = new File(projectManager.getRootPath() + File.separator + "cache"); ModuleManager manager = new DefaultModuleManager(); Collection loadedPlugins = manager.loadModules(pluginDir); pluginManager.setPluginDir(pluginDir.getAbsolutePath()); + pluginManager.setCacheDir(cacheDir.getAbsolutePath()); System.out.println("Plugins directory: " + pluginDir.getAbsolutePath()); System.out.println("Plugins loaded: " + loadedPlugins.size()); diff --git a/src/main/java/games/rednblack/editor/proxy/PluginManager.java b/src/main/java/games/rednblack/editor/proxy/PluginManager.java index bff87459..766eda95 100644 --- a/src/main/java/games/rednblack/editor/proxy/PluginManager.java +++ b/src/main/java/games/rednblack/editor/proxy/PluginManager.java @@ -61,7 +61,7 @@ public class PluginManager extends BaseProxy implements PluginAPI { public static final String NAME = TAG; private ArrayList plugins = new ArrayList<>(); - private String pluginDir; + private String pluginDir, cacheDir; private HashSet pluginEntities; @@ -188,6 +188,15 @@ public class PluginManager extends BaseProxy implements PluginAPI { return pluginDir; } + public void setCacheDir(String cacheDir) { + this.cacheDir = cacheDir; + } + + @Override + public String getCacheDir() { + return cacheDir; + } + @Override public SceneLoader getSceneLoader() { return Sandbox.getInstance().getSceneControl().sceneLoader; diff --git a/src/main/java/games/rednblack/editor/proxy/ProjectManager.java b/src/main/java/games/rednblack/editor/proxy/ProjectManager.java index e9fd3d62..99df1bb7 100755 --- a/src/main/java/games/rednblack/editor/proxy/ProjectManager.java +++ b/src/main/java/games/rednblack/editor/proxy/ProjectManager.java @@ -36,7 +36,7 @@ import games.rednblack.editor.utils.HyperLap2DUtils; import games.rednblack.editor.utils.ZipUtils; import games.rednblack.editor.view.menu.HyperLap2DMenuBar; import games.rednblack.editor.view.stage.Sandbox; -import games.rednblack.editor.view.ui.widget.ProgressHandler; +import games.rednblack.h2d.common.ProgressHandler; import games.rednblack.h2d.common.vo.EditorConfigVO; import games.rednblack.h2d.common.vo.ExportMapperVO; import games.rednblack.h2d.common.vo.ProjectVO; diff --git a/src/main/java/games/rednblack/editor/proxy/ResolutionManager.java b/src/main/java/games/rednblack/editor/proxy/ResolutionManager.java index 2d13b58b..b6587167 100644 --- a/src/main/java/games/rednblack/editor/proxy/ResolutionManager.java +++ b/src/main/java/games/rednblack/editor/proxy/ResolutionManager.java @@ -41,7 +41,7 @@ import com.badlogic.gdx.utils.Array; import com.mortennobel.imagescaling.ResampleOp; import com.puremvc.patterns.proxy.BaseProxy; import games.rednblack.editor.view.stage.Sandbox; -import games.rednblack.editor.view.ui.widget.ProgressHandler; +import games.rednblack.h2d.common.ProgressHandler; import games.rednblack.editor.HyperLap2DFacade; import games.rednblack.editor.renderer.data.ProjectInfoVO; import games.rednblack.editor.renderer.data.ResolutionEntryVO; diff --git a/src/main/java/games/rednblack/editor/utils/AssetImporter.java b/src/main/java/games/rednblack/editor/utils/AssetImporter.java index 01333353..a4bc9d3e 100644 --- a/src/main/java/games/rednblack/editor/utils/AssetImporter.java +++ b/src/main/java/games/rednblack/editor/utils/AssetImporter.java @@ -9,7 +9,7 @@ import games.rednblack.editor.renderer.data.SceneVO; import games.rednblack.editor.view.stage.Sandbox; import games.rednblack.editor.view.ui.dialog.ImportDialog; import games.rednblack.editor.view.ui.dialog.ImportDialogMediator; -import games.rednblack.editor.view.ui.widget.ProgressHandler; +import games.rednblack.h2d.common.ProgressHandler; import java.io.File; diff --git a/src/main/java/games/rednblack/editor/view/ui/dialog/ImportDialogMediator.java b/src/main/java/games/rednblack/editor/view/ui/dialog/ImportDialogMediator.java index eed39b48..ce6abd27 100644 --- a/src/main/java/games/rednblack/editor/view/ui/dialog/ImportDialogMediator.java +++ b/src/main/java/games/rednblack/editor/view/ui/dialog/ImportDialogMediator.java @@ -22,34 +22,23 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; -import com.kotcrab.vis.ui.util.dialog.Dialogs; import com.kotcrab.vis.ui.widget.file.FileChooser; import com.kotcrab.vis.ui.widget.file.FileChooserAdapter; -import com.kotcrab.vis.ui.widget.file.FileTypeFilter; import com.puremvc.patterns.mediator.SimpleMediator; import com.puremvc.patterns.observer.Notification; -import games.rednblack.editor.proxy.SceneDataManager; import games.rednblack.editor.utils.AssetImporter; import games.rednblack.editor.utils.ImportUtils; import games.rednblack.editor.view.frame.FileDropListener; import games.rednblack.editor.view.menu.FileMenu; import games.rednblack.editor.view.stage.Sandbox; -import games.rednblack.editor.view.ui.widget.ProgressHandler; +import games.rednblack.h2d.common.ProgressHandler; import games.rednblack.editor.HyperLap2DFacade; import games.rednblack.editor.proxy.ProjectManager; -import games.rednblack.editor.view.menu.HyperLap2DMenuBar; import games.rednblack.editor.view.stage.UIStage; -import games.rednblack.editor.renderer.data.SceneVO; import games.rednblack.editor.view.ui.widget.ui.HyperLapFileChooser; -import org.apache.commons.lang3.SystemUtils; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DnDConstants; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; -import java.io.File; -import java.util.List; /** * Created by sargis on 4/3/15.