+ * Will search the class for the given file
+ * @param name Script name
+ */
+ public LuaScript(String name) {
+ if (!isValidName(name)) {
+ throw new LuaFileException("Wrong Lua file type");
+ }
+ InputStream stream = getClass().getResourceAsStream(name);
+ try {
+ String source = getSourceFromStream(stream);
+ this.source = source;
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public LuaValue execute(String url) {
+ Globals _G = JsePlatform.standardGlobals();
+ _G.set("httpResponse", url);
+ LuaValue chunk = _G.load(source);
+ return chunk.call();
+ }
+
+}
diff --git a/RobloxLauncher/src/sleitnick/roblox/launcher/RobloxLauncher.java b/RobloxLauncher/src/sleitnick/roblox/launcher/RobloxLauncher.java
new file mode 100644
index 0000000..807a369
--- /dev/null
+++ b/RobloxLauncher/src/sleitnick/roblox/launcher/RobloxLauncher.java
@@ -0,0 +1,65 @@
+package sleitnick.roblox.launcher;
+
+import java.awt.Frame;
+import java.io.File;
+import java.io.IOException;
+
+public final class RobloxLauncher {
+
+ public static final String URL_ROBLOX_VERSION = "http://setup.roblox.com/version";
+ public static final String URL_REQUEST_GAME = "http://www.roblox.com/Game/PlaceLauncher.ashx?request=RequestGame&placeId=#ID";
+ public static final String URL_REQUEST_GROUP_BUILD_GAME = "http://www.roblox.com/Game/PlaceLauncher.ashx?request=RequestGroupBuildGame&placeId=#ID";
+ public static final String URL_DOWNLOAD_ROBLOX = "http://www.roblox.com/install/setup.ashx";
+
+ private static String robloxVersion;
+ static {
+ String response = HttpService.get(URL_ROBLOX_VERSION, false);
+ robloxVersion = response;
+ }
+
+ private static final String LOCAL_APP_DATA = System.getenv("LOCALAPPDATA");
+ private static final File RBX_DIR = new File(LOCAL_APP_DATA, "Roblox/Versions/" + robloxVersion);
+
+
+ /**
+ * Current version installed
+ * @return true if current version is installed
+ */
+ public static boolean hasCurrentVersion() {
+ return RBX_DIR.exists();
+ }
+
+ /**
+ * Launch the given place
+ * @param place Place to launch
+ * @param frame Frame to launch from
+ * @throws RobloxVersionException
+ */
+ protected static void launch(RobloxPlace place, Frame frame) throws RobloxVersionException {
+
+ if (!hasCurrentVersion()) {
+ throw new RobloxVersionException();
+ }
+
+ String joinRequest = HttpService.get((place.isPersonalServer() ? URL_REQUEST_GROUP_BUILD_GAME : URL_REQUEST_GAME).replaceAll("#ID", Integer.toString(place.getPlaceId())), false);
+
+ if (joinRequest.contains("JoinPlace=") || joinRequest.contains("Game/Join.ashx")) {
+ frame.setVisible(false);
+ try {
+ String launchCmd = (RBX_DIR.getAbsolutePath() + "/RobloxPlayerBeta.exe --id " + place.getPlaceId());
+ Process rbxProcess = Runtime.getRuntime().exec(launchCmd);
+ rbxProcess.waitFor();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ frame.setVisible(true);
+ } else {
+ System.out.println("Join request failed");
+ System.out.println(joinRequest);
+ }
+
+ }
+
+}
diff --git a/RobloxLauncher/src/sleitnick/roblox/launcher/RobloxLauncherApp.java b/RobloxLauncher/src/sleitnick/roblox/launcher/RobloxLauncherApp.java
new file mode 100644
index 0000000..785e642
--- /dev/null
+++ b/RobloxLauncher/src/sleitnick/roblox/launcher/RobloxLauncherApp.java
@@ -0,0 +1,227 @@
+package sleitnick.roblox.launcher;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Font;
+import java.awt.Image;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.net.URL;
+
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSeparator;
+import javax.swing.JTextField;
+import javax.swing.UIManager;
+import javax.swing.border.EmptyBorder;
+import javax.swing.text.PlainDocument;
+
+public class RobloxLauncherApp implements ActionListener {
+
+ private JFrame frame;
+ private JPanel panel;
+ private JPanel gamePanel;
+ private JLabel logoLabel;
+ private JLabel idLabel;
+ private JTextField idField;
+ private JButton searchButton;
+ private JLabel gameTitle, gameCreator;
+ private JLabel gameImage;
+ private JButton launchButton;
+ private JButton launchBrowserButton;
+
+ private final Color background = new Color(75, 75, 75);
+
+ private final String ID_INPUT_KEY = "idInput";
+
+ private RobloxPlace currentSearch = null;
+
+ private void createGui() {
+ frame = new JFrame("ROBLOX Launcher");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setResizable(false);
+ frame.getContentPane().setBackground(background);
+ panel = new JPanel();
+ panel.setBorder(new EmptyBorder(10, 10, 10, 10));
+ panel.setBackground(background);
+ gamePanel = new JPanel();
+ gamePanel.setBorder(new EmptyBorder(10, 10, 10, 10));
+ gamePanel.setLayout(new BoxLayout(gamePanel, BoxLayout.PAGE_AXIS));
+ gamePanel.setBackground(background);
+ frame.getContentPane().add(panel, BorderLayout.CENTER);
+ frame.getContentPane().add(gamePanel, BorderLayout.SOUTH);
+ URL logoURL = getClass().getResource("logo.png");
+ ImageIcon logo = new ImageIcon(logoURL);
+ logoLabel = new JLabel(logo);
+ logoLabel.setBorder(new EmptyBorder(10, 10, 10, 10));
+ idLabel = new JLabel("Place ID:");
+ idLabel.setFont(new Font("Arial", Font.BOLD, 14));
+ idLabel.setForeground(new Color(255, 255, 255));
+ idField = new JTextField(20);
+ idField.setFont(new Font("Arial", Font.PLAIN, 12));
+ {
+ Object idObj = DataStoreService.get(ID_INPUT_KEY);
+ if (idObj != null && idObj instanceof Integer) {
+ int id = (Integer)idObj;
+ idField.setText(Integer.toString(id));
+ }
+ }
+ idLabel.setLabelFor(idField);
+ searchButton = new JButton("Search");
+ searchButton.setBackground(background);
+ searchButton.setFont(new Font("Arial", Font.PLAIN, 12));
+ frame.getContentPane().add(logoLabel, BorderLayout.NORTH);
+ panel.add(idLabel);
+ panel.add(idField);
+ panel.add(searchButton);
+ gameTitle = new JLabel();
+ gameTitle.setBorder(new EmptyBorder(10, 0, 0, 0));
+ gameTitle.setFont(new Font("Arial", Font.PLAIN, 18));
+ gameTitle.setForeground(new Color(255, 255, 255));
+ gameCreator = new JLabel();
+ gameCreator.setFont(new Font("Arial", Font.PLAIN, 14));
+ gameCreator.setForeground(new Color(255, 255, 255));
+ gameImage = new JLabel();
+ gameImage.setBorder(new EmptyBorder(10, 0, 10, 0));
+ JSeparator sep = new JSeparator(JSeparator.HORIZONTAL);
+ sep.setBackground(new Color(100, 100, 100));
+ sep.setForeground(background);
+ gamePanel.add(sep);
+ gamePanel.add(gameTitle);
+ gamePanel.add(gameCreator);
+ gamePanel.add(gameImage);
+ launchButton = new JButton("Play");
+ launchButton.setBackground(background);
+ launchButton.setFont(new Font("Arial", Font.BOLD, 14));
+ launchBrowserButton = new JButton("Open in Browser");
+ launchBrowserButton.setBackground(background);
+ launchBrowserButton.setFont(new Font("Arial", Font.PLAIN, 14));
+ gamePanel.add(launchBrowserButton);
+ gamePanel.add(launchButton);
+ frame.pack();
+ frame.setLocation(100, 100);
+ URL iconURL = getClass().getResource("icon.png");
+ ImageIcon icon = new ImageIcon(iconURL);
+ frame.setIconImage(icon.getImage());
+ frame.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ DataStoreService.save();
+ super.windowClosing(e);
+ }
+ });
+ frame.setVisible(true);
+ }
+
+ private void setIdFieldIntegerOnly() {
+ PlainDocument doc = (PlainDocument)idField.getDocument();
+ doc.setDocumentFilter(new IntegerDocumentFilter());
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("launchGame")) {
+ if (!launchButton.isEnabled()) {
+ return;
+ }
+ String idStr = idField.getText().trim();
+ if (!idStr.isEmpty()) {
+ int id = Integer.parseInt(idStr);
+ launchButton.setEnabled(false);
+ try {
+ RobloxPlace place = new RobloxPlace(id);
+ RobloxLauncher.launch(place, frame);
+ } catch(RobloxVersionException rbxVersionException) {
+ Browser.browse(RobloxLauncher.URL_DOWNLOAD_ROBLOX);
+ System.exit(0);
+ } catch (RobloxPlaceException e1) {
+ gameTitle.setText("Invalid place ID");
+ gameCreator.setText("");
+ gameImage.setIcon(null);
+ launchButton.setVisible(false);
+ launchBrowserButton.setVisible(false);
+ }
+ launchButton.setEnabled(true);
+ }
+ } else if (e.getActionCommand().equals("searchGame")) {
+ if (!idField.getText().isEmpty()) {
+ int id = Integer.parseInt(idField.getText());
+ try {
+ frame.getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ idField.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ RobloxPlace place = new RobloxPlace(id);
+ currentSearch = place;
+ gameTitle.setText(place.getPlaceName());
+ gameCreator.setText("Creator: " + place.getCreator());
+ try {
+ URL url = new URL(place.getThumbnail());
+ ImageIcon gameIcon = new ImageIcon(url);
+ Image img = gameIcon.getImage();
+ img = img.getScaledInstance((int)(img.getWidth(null) / 1.2f), (int)(img.getHeight(null) / 1.2f), Image.SCALE_SMOOTH);
+ gameIcon = new ImageIcon(img);
+ gameImage.setIcon(gameIcon);
+ } catch (Exception e1) {
+ gameImage.setIcon(null);
+ }
+ DataStoreService.set(ID_INPUT_KEY, id);
+ launchButton.setVisible(true);
+ launchBrowserButton.setVisible(true);
+ frame.pack();
+ } catch (RobloxPlaceException e1) {
+ gameTitle.setText(e1.getFailedToConnect() ? "Cannot connect to ROBLOX" : "Invalid place ID");
+ gameCreator.setText("");
+ gameImage.setIcon(null);
+ launchButton.setVisible(false);
+ launchBrowserButton.setVisible(false);
+ } finally {
+ frame.getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ idField.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
+ }
+ frame.pack();
+ }
+ } else if (e.getActionCommand().equals("openInBrowser")) {
+ if (currentSearch != null) {
+ currentSearch.openInBrowser();
+ }
+ }
+ }
+
+ public RobloxLauncherApp() {
+ createGui();
+ setIdFieldIntegerOnly();
+ searchButton.setActionCommand("searchGame");
+ searchButton.addActionListener(this);
+ launchButton.setActionCommand("launchGame");
+ launchButton.addActionListener(this);
+ launchBrowserButton.setActionCommand("openInBrowser");
+ launchBrowserButton.addActionListener(this);
+ idField.setActionCommand("searchGame");
+ idField.addActionListener(this);
+ idField.requestFocus();
+ idField.setCaretPosition(idField.getText().length());
+ if (!idField.getText().isEmpty()) {
+ for (ActionListener listener : idField.getActionListeners()) {
+ listener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "searchGame") {
+ private static final long serialVersionUID = 1L;
+ });
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ new RobloxLauncherApp();
+ }
+
+}
diff --git a/RobloxLauncher/src/sleitnick/roblox/launcher/RobloxPlace.java b/RobloxLauncher/src/sleitnick/roblox/launcher/RobloxPlace.java
new file mode 100644
index 0000000..f752b29
--- /dev/null
+++ b/RobloxLauncher/src/sleitnick/roblox/launcher/RobloxPlace.java
@@ -0,0 +1,132 @@
+package sleitnick.roblox.launcher;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+
+import org.luaj.vm2.LuaValue;
+
+
+
+public class RobloxPlace implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private static final String PLACE_URL = "http://www.roblox.com/PlaceItem.aspx?id=#ID";
+
+ private final int placeId;
+ private String placeName;
+ private URI placeUri = null;
+
+ private String creator;
+ private int creatorId;
+
+ private String thumbnail;
+
+ private boolean isPersonalServer;
+
+ private void getPlaceInformation() throws RobloxPlaceException {
+ String url = PLACE_URL.replaceAll("#ID", Integer.toString(placeId));
+ try {
+ placeUri = new URL(url).toURI();
+ } catch (MalformedURLException e1) {
+ } catch (URISyntaxException e1) {
+ }
+ String response = HttpService.get(url, true);
+ if (response.isEmpty()) {
+ throw new RobloxPlaceException("Failed to connect to ROBLOX", placeId, true);
+ }
+ LuaScript parseResponse = new LuaScript("parse_place_response.lua");
+ LuaValue luaResponse = parseResponse.execute(response);
+ if (luaResponse == LuaValue.FALSE) {
+ throw new RobloxPlaceException(placeId);
+ } else {
+ try {
+ placeName = URLDecoder.decode(luaResponse.get("Name").tojstring(), "ASCII");
+ } catch (UnsupportedEncodingException e) {
+ placeName = luaResponse.get("Name").tojstring();
+ }
+ try {
+ thumbnail = URLDecoder.decode(luaResponse.get("Thumbnail").tojstring(), "UTF-8");
+ } catch(UnsupportedEncodingException e) {
+ thumbnail = luaResponse.get("Thumbnail").tojstring();
+ }
+ try {
+ creator = URLDecoder.decode(luaResponse.get("Creator").tojstring(), "UTF-8");
+ } catch(UnsupportedEncodingException e) {
+ creator = luaResponse.get("Creator").tojstring();
+ }
+ creatorId = luaResponse.get("CreatorID").toint();
+ isPersonalServer = luaResponse.get("IsPersonalServer").toboolean();
+ }
+ }
+
+ /**
+ * Reflect a Roblox place
+ * @param placeId Place ID representing the place
+ * @throws RobloxPlaceException
+ */
+ public RobloxPlace(int placeId) throws RobloxPlaceException {
+ this.placeId = placeId;
+ getPlaceInformation();
+ }
+
+ /**
+ * Get the place ID
+ * @return Place ID
+ */
+ public int getPlaceId() {
+ return placeId;
+ }
+
+ /**
+ * Get the place name
+ * @return Place name
+ */
+ public String getPlaceName() {
+ return placeName;
+ }
+
+ /**
+ * Get the creator ID
+ * @return Creator ID
+ */
+ public int getCreatorId() {
+ return creatorId;
+ }
+
+ /**
+ * Get the creator name
+ * @return Creator name
+ */
+ public String getCreator() {
+ return creator;
+ }
+
+ /**
+ * Get the thumbnail URL
+ * @return Thumbnail URL
+ */
+ public String getThumbnail() {
+ return thumbnail;
+ }
+
+ /**
+ * Whether or not the place is a personal server
+ * @return (.-)
]])
+local placeCreatorId, placeCreatorName = httpResponse:match([[(.-)]])
+
+-- Get place thumbnail:
+local placeThumbnail = httpResponse:match([[