Compare commits

...

68 Commits

Author SHA1 Message Date
sirjonasxx
7964aaabbb
Merge pull request #174 from sirjonasxx/dependabot/maven/G-Earth/ch.qos.logback-logback-core-1.3.12
Bump ch.qos.logback:logback-core from 1.3.5 to 1.3.12 in /G-Earth
2024-06-21 00:25:34 +02:00
sirjonasxx
186141c4a0
Merge branch 'master' into dependabot/maven/G-Earth/ch.qos.logback-logback-core-1.3.12 2024-06-21 00:16:28 +02:00
sirjonasxx
1a4543bca9
Merge pull request #173 from sirjonasxx/dependabot/maven/G-Earth/org.json-json-20231013
Bump org.json:json from 20230227 to 20231013 in /G-Earth
2024-06-21 00:14:59 +02:00
sirjonasxx
4a6cc5cc58
Merge pull request #171 from sirjonasxx/dependabot/maven/G-Earth/org.eclipse.jetty-jetty-http-9.4.53.v20231009
Bump org.eclipse.jetty:jetty-http from 9.4.51.v20230217 to 9.4.53.v20231009 in /G-Earth
2024-06-21 00:14:38 +02:00
sirjonasxx
b2ecb33108
Merge pull request #168 from UnfamiliarLegacy/fix/nitro-socket-replacement
Improve Nitro socket url matching
2024-06-21 00:13:32 +02:00
sirjonasxx
e544a3f166
Merge pull request #180 from Laande/patch-1
Change port for habbo.com
2024-06-21 00:12:27 +02:00
sirjonasxx
ef18ff1fd0
Merge pull request #177 from WiredSpast/master
Updated catalog parsers to include Silver
2024-06-21 00:12:14 +02:00
sirjonasxx
2e0faa7598
Merge pull request #178 from WiredSpast/patch-1
Added G-Rust to readme
2024-06-21 00:11:56 +02:00
sirjonasxx
625d31d327
Merge pull request #182 from allombar/master
Implement Nitro Feature for macOS
2024-06-21 00:11:32 +02:00
UnfamiliarLegacy
1fe7bb22e6 Update ws to wss 2024-06-01 18:46:13 +02:00
UnfamiliarLegacy
776d16ed49 Extra cloudflare cookies 2024-05-28 19:31:15 +02:00
UnfamiliarLegacy
88ba87a819 Use SSL WebSockets for Nitro to prevent CSP issues 2024-05-27 19:20:00 +02:00
allombar
ed94340d04 Implement Nitro Feature for macOS 2024-05-19 16:36:17 +02:00
allombar
d4c57ce28b Implement Nitro Feature for macOS 2024-05-19 16:34:09 +02:00
Lande
84cfbac9a4
Change port for habbo.com
ref issue #179
2024-05-01 21:20:32 +02:00
WiredSpast
f48dfb7122
Added G-Rust to readme 2024-03-16 16:14:27 +01:00
WiredSpast
4299428465 Updated catalog parsers to include Silver 2024-02-03 08:51:24 +01:00
UnfamiliarLegacy
bea6848fdc Update socket url matching 2023-12-12 01:21:04 +01:00
UnfamiliarLegacy
9bb98c1739 Update config pattern 2023-12-11 05:01:56 +01:00
dependabot[bot]
fd9f2c32e4
Bump ch.qos.logback:logback-core from 1.3.5 to 1.3.12 in /G-Earth
Bumps [ch.qos.logback:logback-core](https://github.com/qos-ch/logback) from 1.3.5 to 1.3.12.
- [Commits](https://github.com/qos-ch/logback/compare/v_1.3.5...v_1.3.12)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-core
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 22:06:34 +00:00
dependabot[bot]
5180d6cf0b
Bump org.json:json from 20230227 to 20231013 in /G-Earth
Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20230227 to 20231013.
- [Release notes](https://github.com/douglascrockford/JSON-java/releases)
- [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md)
- [Commits](https://github.com/douglascrockford/JSON-java/commits)

---
updated-dependencies:
- dependency-name: org.json:json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-14 23:27:56 +00:00
dependabot[bot]
a5383b01bb
Bump org.eclipse.jetty:jetty-http in /G-Earth
Bumps [org.eclipse.jetty:jetty-http](https://github.com/eclipse/jetty.project) from 9.4.51.v20230217 to 9.4.53.v20231009.
- [Release notes](https://github.com/eclipse/jetty.project/releases)
- [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.51.v20230217...jetty-9.4.53.v20231009)

---
updated-dependencies:
- dependency-name: org.eclipse.jetty:jetty-http
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-10 21:19:39 +00:00
UnfamiliarLegacy
4b42bdf246 Improve Nitro socket url matching 2023-07-15 07:00:21 +02:00
sirjonasxx
8a717ad73f
Merge pull request #167 from UnfamiliarLegacy/fix/nitro-packet-size
Set correct maximum on websocket policy
2023-07-11 22:24:05 +02:00
UnfamiliarLegacy
2c5c3f7db8 Set correct maximum on websocket policy 2023-06-28 22:59:28 +02:00
sirjonasxx
feb1f2a527
Merge pull request #155 from WiredSpast/master
Allow onClick method to be overridden in console extensions
2023-06-16 10:19:58 +02:00
WiredSpast
813fda45e5
Remove faulty changes 2023-06-16 07:57:28 +02:00
WiredSpast
b19d8baab4
Remove faulty changes 2023-06-16 07:56:08 +02:00
sirjonasxx
1add63115c
Merge pull request #158 from sirjonasxx/dependabot/maven/G-Earth/org.json-json-20230227
Bump json from 20190722 to 20230227 in /G-Earth
2023-06-16 07:16:54 +02:00
sirjonasxx
03271c344d
Merge pull request #165 from sirjonasxx/dependabot/maven/G-Earth/org.eclipse.jetty-jetty-server-9.4.51.v20230217
Bump jetty-server from 9.4.50.v20221201 to 9.4.51.v20230217 in /G-Earth
2023-06-16 07:15:13 +02:00
sirjonasxx
930f4d7641
Merge pull request #166 from LilithRainbows/master
Remove Geode from frameworks list
2023-06-16 07:14:51 +02:00
sirjonasxx
8ebd6ac189
Merge pull request #157 from dorving/patch-2
Fixed more typos in the MacOS installation guide
2023-06-16 07:11:40 +02:00
sirjonasxx
e9804afc41
Merge pull request #161 from dorving/trayicon
Added system tray icon support
2023-06-16 07:11:23 +02:00
sirjonasxx
ac9e802d50
Merge pull request #164 from UnfamiliarLegacy/fix/habbox
Improve Nitro reliability and bypass cloudflare challenge on french retro
2023-06-16 07:06:46 +02:00
Lilith
e1acccf303
Remove Geode from frameworks list
No longer mantained, xabbo is a better and updated alternative.
2023-06-14 22:00:27 -03:00
dependabot[bot]
223df57a6a
Bump jetty-server from 9.4.50.v20221201 to 9.4.51.v20230217 in /G-Earth
Bumps [jetty-server](https://github.com/eclipse/jetty.project) from 9.4.50.v20221201 to 9.4.51.v20230217.
- [Release notes](https://github.com/eclipse/jetty.project/releases)
- [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.50.v20221201...jetty-9.4.51.v20230217)

---
updated-dependencies:
- dependency-name: org.eclipse.jetty:jetty-server
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-13 23:10:32 +00:00
UnfamiliarLegacy
1579bed1ef Fix discarded packets 2023-06-07 19:59:55 +02:00
UnfamiliarLegacy
c7037c0441 Better nitro connection state management 2023-06-07 19:35:21 +02:00
UnfamiliarLegacy
df3aabf518 Add log message for packet discard 2023-06-07 19:19:50 +02:00
UnfamiliarLegacy
de39613091 Bypass user-agent based cloudflare challenge 2023-06-07 17:38:05 +02:00
dorving
f6626d1537 Added system tray icon support 2023-05-03 21:09:57 +02:00
WiredSpast
6db7c31bfa Attempt unity fix 2023-04-17 17:49:54 +02:00
WiredSpast
76acc8b391 Merge remote-tracking branch 'origin/master' 2023-04-17 17:47:25 +02:00
WiredSpast
bfc1d3a843 Attempt unity fix 2023-04-17 17:47:15 +02:00
WiredSpast
a8559cfdb1 Attempt unity fix 2023-04-17 17:36:41 +02:00
dependabot[bot]
a50fea88f4
Bump json from 20190722 to 20230227 in /G-Earth
Bumps [json](https://github.com/douglascrockford/JSON-java) from 20190722 to 20230227.
- [Release notes](https://github.com/douglascrockford/JSON-java/releases)
- [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md)
- [Commits](https://github.com/douglascrockford/JSON-java/commits)

---
updated-dependencies:
- dependency-name: org.json:json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-14 17:55:25 +00:00
dorving
a34d16f1cc
Added troubleshooting step 2023-04-10 21:00:08 +02:00
dorving
cb786d4749
Fixed more typos in the MacOS installation guide 2023-04-10 20:32:59 +02:00
WiredSpast
3ee8f55766
Attempt onClick fix 2023-04-04 03:42:06 +02:00
WiredSpast
22f648ec6d
Attempt onClick fix 2023-04-04 03:40:02 +02:00
WiredSpast
ee73ae1d41
Attempt onClick fix 2023-04-04 03:37:54 +02:00
sirjonasxx
8de3d5cdd4
Merge pull request #151 from WiredSpast/master
Reformat StuffData
2023-03-30 22:12:26 +02:00
sirjonasxx
df12a945f4
Merge pull request #153 from dorving/patch-1
Update MacOS installation guide
2023-03-30 22:08:19 +02:00
dorving
1b72d600a3
Fixed typo 2023-03-30 15:09:08 +02:00
dorving
c06c4d41bb
Added link to guide for disabling SIP 2023-03-30 15:08:26 +02:00
dorving
150afc9ce1
Update macOs-Installation-guide.md
Updated steps with screenshots in English and added a guide for making the `G-Mem` file executable and disabling SIP.
2023-03-30 15:06:07 +02:00
sirjonasxx
a868ab512c
Merge pull request #152 from XePeleato/whatever
uilogger: Fix "View Outgoing" label behaviour
2023-03-24 09:14:18 +01:00
Eduardo Alonso
d96c468103 uilogger: Fix "View Outgoing" label behaviour 2023-03-23 19:03:09 +01:00
WiredSpast
4c7b924268 Deleted old HStuff class 2023-03-19 12:46:55 +01:00
WiredSpast
661ef48076 Added scoretype names and cleartype names in comments 2023-03-19 12:45:12 +01:00
WiredSpast
413c76915f Reformat StuffData 2023-03-19 12:14:58 +01:00
WiredSpast
aec5d6a137 Reformat StuffData 2023-03-19 11:52:01 +01:00
WiredSpast
43c15fed3e
Potential fix 2023-02-24 17:30:23 +01:00
sirjonasxx
6a9b1201ac
Merge pull request #147 from erikmoch/patch-1
Update messages_pt.properties
2023-02-21 01:34:28 +01:00
sirjonasxx
199fbed0dc
Merge pull request #149 from UnfamiliarLegacy/fix/nitro-ssl-2
Disable SSL certificate validation for Nitro websocket connection
2023-02-21 01:33:30 +01:00
UnfamiliarLegacy
8a880a13df Allow untrusted SSL certificates for Nitro 2023-02-13 05:03:56 +01:00
UnfamiliarLegacy
16749e9e96 Update jetty and drop javax.websocket-api 2023-02-13 03:32:34 +01:00
Moch
d8c5c8ebc0
Update messages_pt.properties
I carried out some orthographic corrections and translated new texts.
2023-02-05 00:20:17 -03:00
43 changed files with 1721 additions and 313 deletions

View File

@ -10,8 +10,8 @@
<properties>
<javafx.version>1.8</javafx.version>
<jettyVersion>9.4.48.v20220622</jettyVersion>
<logback.version>1.3.5</logback.version>
<jettyVersion>9.4.53.v20231009</jettyVersion>
<logback.version>1.3.12</logback.version>
</properties>
<parent>
@ -209,7 +209,7 @@
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20190722</version>
<version>20231013</version>
</dependency>
<dependency>
<groupId>org.fxmisc.richtext</groupId>
@ -238,14 +238,11 @@
<artifactId>maven-artifact</artifactId>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>

View File

@ -5,6 +5,7 @@ import gearth.misc.Cacher;
import gearth.misc.UpdateChecker;
import gearth.misc.listenerpattern.ObservableObject;
import gearth.ui.GEarthController;
import gearth.ui.GEarthTrayIcon;
import gearth.ui.themes.Theme;
import gearth.ui.themes.ThemeFactory;
import gearth.ui.titlebar.TitleBarConfig;
@ -41,7 +42,6 @@ public class GEarth extends Application {
public void start(Stage primaryStage) throws Exception{
main = this;
stage = primaryStage;
FXMLLoader loader = new FXMLLoader(getClass().getResource("/gearth/ui/G-Earth.fxml"));
Parent root;
try {
@ -120,7 +120,8 @@ public class GEarth extends Application {
stage.getScene().getStylesheets().add(GEarth.class.getResource(String.format("/gearth/ui/themes/%s/styling.css", theme.internalName())).toExternalForm());
stage.getIcons().clear();
stage.getIcons().add(new Image(GEarth.class.getResourceAsStream(String.format("/gearth/ui/themes/%s/logoSmall.png", theme.overridesLogo() ? theme.internalName() : defaultTheme.internalName()))));
final Image image = new Image(GEarth.class.getResourceAsStream(String.format("/gearth/ui/themes/%s/logoSmall.png", theme.overridesLogo() ? theme.internalName() : defaultTheme.internalName())));
stage.getIcons().add(image);
stage.setTitle((theme.overridesTitle() ? theme.title() : defaultTheme.title()) + " " + GEarth.version);
controller.infoController.img_logo.setImage(new Image(GEarth.class.getResourceAsStream(
@ -131,7 +132,11 @@ public class GEarth extends Application {
)));
controller.infoController.version.setText(stage.getTitle());
// });
GEarthTrayIcon.updateOrCreate(image);
}
public GEarthController getController() {
return controller;
}
public static String[] args;

View File

@ -169,9 +169,7 @@ public abstract class ExtensionBase extends IExtension {
* The application got doubleclicked from the G-Earth interface. Doing something here is optional
*/
@Override
void onClick() {
}
protected void onClick() {}
@Override
protected ExtensionInfo getInfoAnnotations() {

View File

@ -67,7 +67,7 @@ public abstract class ExtensionForm extends ExtensionBase {
/**
* The application got doubleclicked from the G-Earth interface. Doing something here is optional
*/
public void onClick(){
public final void onClick(){
Platform.runLater(() -> {
primaryStage.show();
primaryStage.requestFocus();

View File

@ -1,10 +1,9 @@
package gearth.extensions.parsers;
import gearth.extensions.parsers.stuffdata.IStuffData;
import gearth.protocol.HPacket;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class HFloorItem implements IFurni {
private int id;
@ -12,17 +11,15 @@ public class HFloorItem implements IFurni {
private HPoint tile;
private HDirection facing;
private int category;
private int secondsToExpiration;
private int usagePolicy;
private int ownerId;
private String ownerName;
private Object[] stuff;
private IStuffData stuff;
private String ignore1;
private Integer ignore2;
private String ignore3;
private String sizeZ;
private Integer extra;
private String staticClass;
public HFloorItem(HPacket packet) {
id = packet.readInteger();
@ -34,12 +31,10 @@ public class HFloorItem implements IFurni {
tile = new HPoint(x, y, Double.parseDouble(packet.readString()));
ignore1 = packet.readString();
ignore2 = packet.readInteger();
sizeZ = packet.readString();
extra = packet.readInteger();
category = packet.readInteger();
stuff = HStuff.readData(packet, category);
stuff = IStuffData.read(packet);
secondsToExpiration = packet.readInteger();
usagePolicy = packet.readInteger();
@ -47,13 +42,11 @@ public class HFloorItem implements IFurni {
ownerId = packet.readInteger();
if (typeId < 0) {
ignore3 = packet.readString();
staticClass = packet.readString();
}
else {
ignore3 = null;
staticClass = null;
}
}
public void appendToPacket(HPacket packet) {
@ -76,20 +69,14 @@ public class HFloorItem implements IFurni {
packet.appendString(tile.getZ() + "");
// ignore1 = packet.readString();
packet.appendString(ignore1);
// sizeZ = packet.readString();
packet.appendString(sizeZ);
// ignore2 = packet.readInteger();
packet.appendInt(ignore2);
// extra = packet.readInteger();
packet.appendInt(extra);
// category = packet.readInteger();
packet.appendInt(category);
// stuff = HStuff.readData(packet, category);
for (Object object : stuff) {
packet.appendObject(object);
}
// stuff = IStuffData.read(packet);
stuff.appendToPacket(packet);
// secondsToExpiration = packet.readInteger();
packet.appendInt(secondsToExpiration);
@ -102,8 +89,8 @@ public class HFloorItem implements IFurni {
if (typeId < 0) {
// ignore3 = packet.readString();
packet.appendString(ignore3);
// staticClass = packet.readString();
packet.appendString(staticClass);
}
}
@ -174,9 +161,9 @@ public class HFloorItem implements IFurni {
return secondsToExpiration;
}
public int getCategory() {
return category;
}
// public int getCategory() {
// return category;
// }
public HDirection getFacing() {
return facing;
@ -186,7 +173,7 @@ public class HFloorItem implements IFurni {
return tile;
}
public Object[] getStuff() {
public IStuffData getStuff() {
return stuff;
}
@ -210,9 +197,9 @@ public class HFloorItem implements IFurni {
this.facing = facing;
}
public void setCategory(int category) {
this.category = category;
}
// public void setCategory(int category) {
// this.category = category;
// }
public void setSecondsToExpiration(int secondsToExpiration) {
this.secondsToExpiration = secondsToExpiration;
@ -226,7 +213,7 @@ public class HFloorItem implements IFurni {
this.ownerId = ownerId;
}
public void setStuff(Object[] stuff) {
public void setStuff(IStuffData stuff) {
this.stuff = stuff;
}
}

View File

@ -1,5 +1,6 @@
package gearth.extensions.parsers;
import gearth.extensions.parsers.stuffdata.IStuffData;
import gearth.protocol.HPacket;
public class HInventoryItem {
@ -8,8 +9,7 @@ public class HInventoryItem {
private int id;
private int typeId;
private HSpecialType specialType;
private int category;
private Object[] stuff;
private IStuffData stuff;
private boolean recyclable;
private boolean tradeable;
private boolean groupable;
@ -27,8 +27,7 @@ public class HInventoryItem {
id = packet.readInteger();
typeId = packet.readInteger();
specialType = HSpecialType.fromId(packet.readInteger());
category = packet.readInteger();
stuff = HStuff.readData(packet, category);
stuff = IStuffData.read(packet);
recyclable = packet.readBoolean();
tradeable = packet.readBoolean();
groupable = packet.readBoolean();
@ -49,11 +48,7 @@ public class HInventoryItem {
packet.appendInt(id);
packet.appendInt(typeId);
packet.appendInt(specialType.getId());
packet.appendInt(category);
for (Object object : stuff) {
packet.appendObject(object);
}
stuff.appendToPacket(packet);
packet.appendBoolean(recyclable);
packet.appendBoolean(tradeable);
@ -133,19 +128,11 @@ public class HInventoryItem {
this.typeId = typeId;
}
public int getCategory() {
return category;
}
public void setCategory(int category) {
this.category = category;
}
public Object[] getStuff() {
public IStuffData getStuff() {
return stuff;
}
public void setStuff(Object[] stuff) {
public void setStuff(IStuffData stuff) {
this.stuff = stuff;
}

View File

@ -1,91 +0,0 @@
package gearth.extensions.parsers;
import gearth.protocol.HPacket;
import java.util.ArrayList;
import java.util.List;
public class HStuff {
public static Object[] readData(HPacket packet, int category) {
List<Object> values = new ArrayList<>();
switch (category & 0xFF)
{
case 0: /* LegacyStuffData */ {
values.add(packet.readString());
break;
}
case 1: /* MapStuffData */ {
int count = packet.readInteger();
values.add(count);
for (int j = 0; j < count; j++)
{
values.add(packet.readString());
values.add(packet.readString());
}
break;
}
case 2: /* StringArrayStuffData */ {
int count = packet.readInteger();
values.add(count);
for (int j = 0; j < count; j++)
{
values.add(packet.readString());
}
break;
}
case 3: /* VoteResultStuffData */ {
values.add(packet.readString());
values.add(packet.readInteger());
break;
}
case 5: /* IntArrayStuffData */ {
int count = packet.readInteger();
values.add(count);
for (int j = 0; j < count; j++)
{
values.add(packet.readInteger());
}
break;
}
case 6: /* HighScoreStuffData */ {
values.add(packet.readString());
values.add(packet.readInteger());
values.add(packet.readInteger());
int count = packet.readInteger();
values.add(count);
for (int j = 0; j < count; j++)
{
int score = packet.readInteger();
values.add(score);
int subCount = packet.readInteger();
values.add(subCount);
for (int k = 0; k < subCount; k++)
{
values.add(packet.readString());
}
}
break;
}
case 7: /* CrackableStuffData */ {
values.add(packet.readString());
values.add(packet.readInteger());
values.add(packet.readInteger());
break;
}
}
if ((category & 0xFF00 & 0x100) > 0) {
values.add(packet.readInteger());
values.add(packet.readInteger());
}
return values.toArray();
}
}

View File

@ -13,6 +13,7 @@ public class HOffer {
private int priceInCredits;
private int priceInActivityPoints;
private int activityPointType;
private int priceInSilver;
private boolean giftable;
private List<HProduct> products = new ArrayList<>();
private int clubLevel;
@ -28,6 +29,7 @@ public class HOffer {
this.priceInCredits = packet.readInteger();
this.priceInActivityPoints = packet.readInteger();
this.activityPointType = packet.readInteger();
this.priceInSilver = packet.readInteger();
this.giftable = packet.readBoolean();
int productCount = packet.readInteger();
@ -65,6 +67,10 @@ public class HOffer {
return activityPointType;
}
public int getPriceInSilver() {
return priceInSilver;
}
public boolean isGiftable() {
return giftable;
}

View File

@ -0,0 +1,173 @@
package gearth.extensions.parsers.stuffdata;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
/**
* Wrapper class to make IntArrayStuffData and StringArrayStuffData behave like a List
* @param <T> Value class
*/
public class ArrayStuffData<T> extends StuffDataBase implements List<T> {
protected List<T> values = new ArrayList<>();
public ArrayStuffData() {
super();
}
public ArrayStuffData(int uniqueSerialNumber, int uniqueSerialSize) {
super(uniqueSerialNumber, uniqueSerialSize);
}
@Override
public int size() {
return values.size();
}
@Override
public boolean isEmpty() {
return values.isEmpty();
}
@Override
public boolean contains(Object o) {
return values.contains(o);
}
@Override
public Iterator<T> iterator() {
return values.iterator();
}
@Override
public void forEach(Consumer<? super T> action) {
values.forEach(action);
}
@Override
public Object[] toArray() {
return values.toArray();
}
@Override
public <T1> T1[] toArray(T1[] a) {
return values.toArray(a);
}
@Override
public boolean add(T t) {
return values.add(t);
}
@Override
public boolean remove(Object o) {
return values.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return values.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends T> c) {
return values.addAll(c);
}
@Override
public boolean addAll(int index, Collection<? extends T> c) {
return values.addAll(index, c);
}
@Override
public boolean removeAll(Collection<?> c) {
return values.removeAll(c);
}
@Override
public boolean removeIf(Predicate<? super T> filter) {
return values.removeIf(filter);
}
@Override
public boolean retainAll(Collection<?> c) {
return values.retainAll(c);
}
@Override
public void replaceAll(UnaryOperator<T> operator) {
values.replaceAll(operator);
}
@Override
public void sort(Comparator<? super T> c) {
values.sort(c);
}
@Override
public void clear() {
values.clear();
}
@Override
public T get(int index) {
return values.get(index);
}
@Override
public T set(int index, T element) {
return values.set(index, element);
}
@Override
public void add(int index, T element) {
values.add(index, element);
}
@Override
public T remove(int index) {
return values.remove(index);
}
@Override
public int indexOf(Object o) {
return values.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return values.lastIndexOf(o);
}
@Override
public ListIterator<T> listIterator() {
return values.listIterator();
}
@Override
public ListIterator<T> listIterator(int index) {
return values.listIterator(index);
}
@Override
public List<T> subList(int fromIndex, int toIndex) {
return values.subList(fromIndex, toIndex);
}
@Override
public Spliterator<T> spliterator() {
return values.spliterator();
}
@Override
public Stream<T> stream() {
return values.stream();
}
@Override
public Stream<T> parallelStream() {
return values.parallelStream();
}
}

View File

@ -0,0 +1,73 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
public class CrackableStuffData extends StuffDataBase {
public final static int IDENTIFIER = 7;
private String legacyString = "";
private int hits = 0;
private int target = 0;
protected CrackableStuffData() {}
public CrackableStuffData(String legacyString, int hits, int target) {
super();
this.legacyString = legacyString == null ? "" : legacyString;
this.hits = hits;
this.target = target;
}
public CrackableStuffData(int uniqueSerialNumber, int uniqueSerialSize, String legacyString, int hits, int target) {
super(uniqueSerialNumber, uniqueSerialSize);
this.legacyString = legacyString == null ? "" : legacyString;
this.hits = hits;
this.target = target;
}
@Override
protected void initialize(HPacket packet) {
this.legacyString = packet.readString();
this.hits = packet.readInteger();
this.target = packet.readInteger();
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendObjects(
this.legacyString,
this.hits,
this.target
);
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.legacyString;
}
@Override
public void setLegacyString(String legacyString) {
this.legacyString = legacyString;
}
public int getHits() {
return this.hits;
}
public void setHits(int hits) {
this.hits = hits;
}
public int getTarget() {
return this.target;
}
public void setTarget(int target) {
this.target = target;
}
}

View File

@ -0,0 +1,26 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
public class EmptyStuffData extends StuffDataBase {
public final static int IDENTIFIER = 4;
public EmptyStuffData() {
super();
}
public EmptyStuffData(int uniqueSerialNumber, int uniqueSerialSize) {
super(uniqueSerialNumber, uniqueSerialSize);
}
@Override
protected void initialize(HPacket packet) {
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
super.appendToPacket(packet);
}
}

View File

@ -0,0 +1,53 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import java.util.Arrays;
import java.util.Objects;
public class HighScoreData {
private int score;
private String[] users;
protected HighScoreData(HPacket packet) {
this.score = packet.readInteger();
int size = packet.readInteger();
this.users = new String[size];
for (int i = 0; i < size; i++) {
this.users[i] = packet.readString();
}
}
public HighScoreData(int score, String... users) {
super();
this.score = score;
this.users = Arrays.stream(users).filter(Objects::nonNull).toArray(String[]::new);
}
protected void appendToPacket(HPacket packet) {
packet.appendInt(this.score);
packet.appendInt(this.users.length);
for (String user : this.users) {
packet.appendString(user);
}
}
public int getScore() {
return this.score;
}
public void setScore(int score) {
this.score = score;
}
public String[] getUsers() {
return this.users.clone();
}
public void setUsers(String[] users) {
this.users = Arrays
.stream(users == null ? new String[0] : users)
.filter(Objects::nonNull)
.toArray(String[]::new);
}
}

View File

@ -0,0 +1,99 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import java.util.Arrays;
import java.util.Objects;
public class HighScoreStuffData extends StuffDataBase {
public final static int IDENTIFIER = 6;
private String legacyString = "";
// ['perteam', 'mostwins', 'classic', 'fastesttime', 'longesttime']
private int scoreType = 0;
// ['alltime', 'daily', 'weekly', 'monthly']
private int clearType = 0;
private HighScoreData[] entries = {};
protected HighScoreStuffData() {}
public HighScoreStuffData(String legacyString, int scoreType, int clearType, HighScoreData... entries) {
super();
this.legacyString = legacyString == null ? "" : legacyString;
this.scoreType = scoreType;
this.clearType = clearType;
this.entries = Arrays.stream(entries).filter(Objects::nonNull).toArray(HighScoreData[]::new);
}
public HighScoreStuffData(int uniqueSerialNumber, int uniqueSerialSize, String legacyString, int scoreType, int clearType, HighScoreData... entries) {
super(uniqueSerialNumber, uniqueSerialSize);
this.legacyString = legacyString == null ? "" : legacyString;
this.scoreType = scoreType;
this.clearType = clearType;
this.entries = Arrays.stream(entries).filter(Objects::nonNull).toArray(HighScoreData[]::new);
}
@Override
protected void initialize(HPacket packet) {
this.legacyString = packet.readString();
this.scoreType = packet.readInteger();
this.clearType = packet.readInteger();
int size = packet.readInteger();
this.entries = new HighScoreData[size];
for (int i = 0; i < size; i++) {
this.entries[i] = new HighScoreData(packet);
}
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendObjects(
this.legacyString,
this.scoreType,
this.clearType,
this.entries.length
);
for (HighScoreData entry : this.entries) {
entry.appendToPacket(packet);
}
}
@Override
public String getLegacyString() {
return this.legacyString;
}
@Override
public void setLegacyString(String legacyString) {
this.legacyString = legacyString;
}
public int getScoreType() {
return this.scoreType;
}
public void setScoreType(int scoreType) {
this.scoreType = scoreType;
}
public int getClearType() {
return this.clearType;
}
public void setClearType(int clearType) {
this.clearType = clearType;
}
public HighScoreData[] getEntries() {
return this.entries.clone();
}
public void setEntries(HighScoreData[] entries) {
this.entries = Arrays
.stream(entries == null ? new HighScoreData[0] : entries)
.filter(Objects::nonNull)
.toArray(HighScoreData[]::new);
}
}

View File

@ -0,0 +1,66 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
public interface IStuffData {
static IStuffData read(HPacket packet) {
int a = packet.readInteger();
StuffDataBase stuffData = null;
switch (a & 255) {
case LegacyStuffData.IDENTIFIER:
stuffData = new LegacyStuffData();
break;
case MapStuffData.IDENTIFIER:
stuffData = new MapStuffData();
break;
case StringArrayStuffData.IDENTIFIER:
stuffData = new StringArrayStuffData();
break;
case VoteResultStuffData.IDENTIFIER:
stuffData = new VoteResultStuffData();
break;
case EmptyStuffData.IDENTIFIER:
stuffData = new EmptyStuffData();
break;
case IntArrayStuffData.IDENTIFIER:
stuffData = new IntArrayStuffData();
break;
case HighScoreStuffData.IDENTIFIER:
stuffData = new HighScoreStuffData();
break;
case CrackableStuffData.IDENTIFIER:
stuffData = new CrackableStuffData();
break;
}
if (stuffData != null) {
stuffData.setFlags(a & 65280);
stuffData.initialize(packet);
} else {
throw new RuntimeException("Unknown stuffdata type");
}
return stuffData;
}
void appendToPacket(HPacket packet);
String getLegacyString();
void setLegacyString(String legacyString);
void setFlags(int flags);
int getFlags();
int getUniqueSerialNumber();
int getUniqueSerialSize();
void setUniqueSerialNumber(int uniqueSerialNumber);
void setUniqueSerialSize(int uniqueSerialSize);
int getRarityLevel();
int getState();
void setState(int state);
String getJSONValue(String key);
}

View File

@ -0,0 +1,53 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import java.util.Arrays;
// 5
public class IntArrayStuffData extends ArrayStuffData<Integer> {
public final static int IDENTIFIER = 5;
protected IntArrayStuffData() {}
public IntArrayStuffData(Integer... values) {
super();
this.values = Arrays.asList(values);
}
public IntArrayStuffData(int uniqueSerialNumber, int uniqueSerialSize, Integer... values) {
super(uniqueSerialNumber, uniqueSerialSize);
this.values = Arrays.asList(values);
}
@Override
protected void initialize(HPacket packet) {
int size = packet.readInteger();
this.clear();
for (int i = 0; i < size; i++) {
this.add(packet.readInteger());
}
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendInt(this.size());
for (int i : this) {
packet.appendInt(i);
}
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.size() > 0 ? String.valueOf(this.get(0)) : "";
}
@Override
public void setLegacyString(String legacyString) {
if (this.size() > 0)
this.set(0, Integer.parseInt(legacyString));
}
}

View File

@ -0,0 +1,43 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
public class LegacyStuffData extends StuffDataBase {
public final static int IDENTIFIER = 0;
private String legacyString;
protected LegacyStuffData() {}
public LegacyStuffData(String legacyString) {
this.legacyString = legacyString == null ? "" : legacyString;
}
public LegacyStuffData(int uniqueSerialNumber, int uniqueSerialSize, String legacyString) {
super(uniqueSerialNumber, uniqueSerialSize);
this.legacyString = legacyString == null ? "" : legacyString;
}
@Override
protected void initialize(HPacket packet) {
this.legacyString = packet.readString();
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendString(this.legacyString);
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.legacyString;
}
@Override
public void setLegacyString(String legacyString) {
this.legacyString = legacyString;
}
}

View File

@ -0,0 +1,177 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
public class MapStuffData extends StuffDataBase implements Map<String, String> {
public final static int IDENTIFIER = 1;
private Map<String, String> map = new HashMap<>();
protected MapStuffData() {}
public MapStuffData(HashMap<String, String> map) {
this.map = map == null ? new HashMap<>() : map.entrySet().stream().filter(e -> e.getValue() != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, HashMap::new));
}
public MapStuffData(int uniqueSerialNumber, int uniqueSerialSize, HashMap<String, String> map) {
super(uniqueSerialNumber, uniqueSerialSize);
this.map = map == null ? new HashMap<>() : map.entrySet().stream().filter(e -> e.getValue() != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, HashMap::new));
}
@Override
protected void initialize(HPacket packet) {
int size = packet.readInteger();
this.clear();
for (int i = 0; i < size; i++) {
this.put(packet.readString(), packet.readString());
}
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendInt(this.size());
for (Entry<String, String> entry : this.entrySet()) {
packet.appendObjects(
entry.getKey(),
entry.getValue() == null ? "" : entry.getValue()
);
}
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.getOrDefault("state", "");
}
@Override
public void setLegacyString(String legacyString) {
this.put("state", legacyString == null ? "" : legacyString);
}
@Override
public int size() {
return this.map.size();
}
@Override
public boolean isEmpty() {
return this.map.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return this.map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return this.map.containsValue(value);
}
@Override
public String get(Object key) {
return this.map.get(key);
}
@Override
public String put(String key, String value) {
return this.map.put(key, value);
}
@Override
public String remove(Object key) {
return this.map.remove(key);
}
@Override
public void putAll(Map<? extends String, ? extends String> m) {
this.map.putAll(m);
}
@Override
public void clear() {
this.map.clear();
}
@Override
public Set<String> keySet() {
return this.map.keySet();
}
@Override
public Collection<String> values() {
return this.map.values();
}
@Override
public Set<Entry<String, String>> entrySet() {
return this.map.entrySet();
}
@Override
public String getOrDefault(Object key, String defaultValue) {
return this.map.getOrDefault(key, defaultValue);
}
@Override
public void forEach(BiConsumer<? super String, ? super String> action) {
this.map.forEach(action);
}
@Override
public void replaceAll(BiFunction<? super String, ? super String, ? extends String> function) {
this.map.replaceAll(function);
}
@Override
public String putIfAbsent(String key, String value) {
return this.map.putIfAbsent(key, value);
}
@Override
public boolean remove(Object key, Object value) {
return this.map.remove(key, value);
}
@Override
public boolean replace(String key, String oldValue, String newValue) {
return this.map.replace(key, oldValue, newValue);
}
@Override
public String replace(String key, String value) {
return this.map.replace(key, value);
}
@Override
public String computeIfAbsent(String key, Function<? super String, ? extends String> mappingFunction) {
return this.map.computeIfAbsent(key, mappingFunction);
}
@Override
public String computeIfPresent(String key, BiFunction<? super String, ? super String, ? extends String> remappingFunction) {
return this.map.computeIfPresent(key, remappingFunction);
}
@Override
public String compute(String key, BiFunction<? super String, ? super String, ? extends String> remappingFunction) {
return this.map.compute(key, remappingFunction);
}
@Override
public String merge(String key, String value, BiFunction<? super String, ? super String, ? extends String> remappingFunction) {
return this.map.merge(key, value, remappingFunction);
}
}

View File

@ -0,0 +1,55 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;
// 2
public class StringArrayStuffData extends ArrayStuffData<String> {
public final static int IDENTIFIER = 2;
protected StringArrayStuffData() {}
public StringArrayStuffData(String... array) {
this.values = Arrays.stream(array).filter(Objects::nonNull).collect(Collectors.toList());
}
public StringArrayStuffData(int uniqueSerialNumber, int uniqueSerialSize, String... array) {
super(uniqueSerialNumber, uniqueSerialSize);
this.values = Arrays.stream(array).filter(Objects::nonNull).collect(Collectors.toList());
}
@Override
protected void initialize(HPacket packet) {
int size = packet.readInteger();
this.clear();
for (int i = 0; i < size; i++) {
this.add(packet.readString());
}
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendInt(this.size());
for (String s : this) {
packet.appendString(s);
}
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.size() > 0 ? this.get(0) : "";
}
@Override
public void setLegacyString(String legacyString) {
if (this.size() > 0) {
this.set(0, legacyString == null ? "" : legacyString);
}
}
}

View File

@ -0,0 +1,98 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import org.json.JSONObject;
public abstract class StuffDataBase implements IStuffData {
private int flags = 0;
private int uniqueSerialNumber = 0;
private int uniqueSerialSize = 0;
protected StuffDataBase() {}
protected StuffDataBase(int uniqueSerialNumber, int uniqueSerialSize) {
this.uniqueSerialNumber = uniqueSerialNumber;
this.uniqueSerialSize = uniqueSerialSize;
flags = 256;
}
protected void initialize(HPacket packet) {
if ((flags & 256) > 0) {
this.uniqueSerialNumber = packet.readInteger();
this.uniqueSerialSize = packet.readInteger();
}
}
public void appendToPacket(HPacket packet) {
if ((flags & 256) > 0) {
packet.appendInt(this.uniqueSerialNumber);
packet.appendInt(this.uniqueSerialSize);
}
}
@Override
public String getLegacyString() {
return "";
}
@Override
public void setLegacyString(String legacyString) {}
@Override
public final void setFlags(int flags) {
this.flags = flags;
}
@Override
public final int getFlags() {
return this.flags;
}
@Override
public final int getUniqueSerialNumber() {
return this.uniqueSerialNumber;
}
@Override
public final int getUniqueSerialSize() {
return this.uniqueSerialSize;
}
@Override
public final void setUniqueSerialNumber(int uniqueSerialNumber) {
this.uniqueSerialNumber = uniqueSerialNumber;
}
@Override
public final void setUniqueSerialSize(int uniqueSerialSize) {
this.uniqueSerialSize = uniqueSerialSize;
}
@Override
public int getRarityLevel() {
return -1;
}
@Override
public final int getState() {
try {
return Integer.parseInt(getLegacyString());
} catch (Exception e) {
return -1;
}
}
@Override
public final void setState(int state) {
setLegacyString(String.valueOf(state));
}
@Override
public final String getJSONValue(String key) {
try {
return new JSONObject(getLegacyString()).getString(key);
} catch (Exception e) {
return "";
}
}
}

View File

@ -0,0 +1,58 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
public class VoteResultStuffData extends StuffDataBase {
public static final int IDENTIFIER = 3;
private String legacyString = "";
private int result = 0;
protected VoteResultStuffData() {}
public VoteResultStuffData(String legacyString, int result) {
this.legacyString = legacyString == null ? "" : legacyString;
this.result = result;
}
public VoteResultStuffData(int uniqueSerialNumber, int uniqueSerialSize, String legacyString, int result) {
super(uniqueSerialNumber, uniqueSerialSize);
this.legacyString = legacyString == null ? "" : legacyString;
this.result = result;
}
@Override
protected void initialize(HPacket packet) {
this.legacyString = packet.readString();
this.result = packet.readInteger();
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendObjects(
this.legacyString,
this.result
);
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.legacyString;
}
@Override
public void setLegacyString(String legacyString) {
this.legacyString = legacyString == null ? "" : legacyString;
}
public int getResult() {
return this.result;
}
public void setResult(int result) {
this.result = result;
}
}

View File

@ -38,7 +38,7 @@ public class ProxyProviderFactory {
autoDetectHosts.add("game-it.habbo.com:30000");
autoDetectHosts.add("game-nl.habbo.com:30000");
autoDetectHosts.add("game-tr.habbo.com:30000");
autoDetectHosts.add("game-us.habbo.com:38101");
autoDetectHosts.add("game-us.habbo.com:30000");
autoDetectHosts.add("game-s2.habbo.com:30000");
List<Object> additionalCachedHotels = Cacher.getList(HOTELS_CACHE_KEY);

View File

@ -0,0 +1,66 @@
package gearth.protocol.connection.proxy.nitro;
import gearth.protocol.HMessage;
import gearth.protocol.connection.HState;
import gearth.protocol.connection.HStateSetter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NitroConnectionState {
private static final Logger logger = LoggerFactory.getLogger(NitroConnectionState.class);
private final HStateSetter stateSetter;
private boolean aborting;
private boolean toServer;
private boolean toClient;
public NitroConnectionState(HStateSetter stateSetter) {
this.stateSetter = stateSetter;
}
public void setConnected(HMessage.Direction direction) {
if (direction == HMessage.Direction.TOCLIENT) {
this.toClient = true;
} else if (direction == HMessage.Direction.TOSERVER) {
this.toServer = true;
}
this.checkConnected();
}
public boolean isConnected() {
if (this.aborting) {
return false;
}
if (!this.toClient) {
return false;
}
if (!this.toServer) {
return false;
}
return true;
}
private void checkConnected() {
if (!this.isConnected()) {
return;
}
this.stateSetter.setState(HState.CONNECTED);
logger.info("Connected");
}
public void setAborting() {
this.aborting = true;
this.stateSetter.setState(HState.ABORTING);
logger.info("Aborting");
}
}

View File

@ -0,0 +1,28 @@
package gearth.protocol.connection.proxy.nitro;
import gearth.protocol.packethandler.nitro.NitroPacketHandler;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
public class NitroPacketQueue {
private final NitroPacketHandler packetHandler;
private final Queue<byte[]> packets;
public NitroPacketQueue(NitroPacketHandler packetHandler) {
this.packetHandler = packetHandler;
this.packets = new LinkedList<>();
}
public void enqueue(byte[] b) {
this.packets.add(b);
}
public synchronized void flush() throws IOException {
while (!this.packets.isEmpty()) {
this.packetHandler.act(this.packets.remove());
}
}
}

View File

@ -6,6 +6,8 @@ import gearth.protocol.connection.HProxySetter;
import gearth.protocol.connection.HState;
import gearth.protocol.connection.HStateSetter;
import gearth.protocol.connection.proxy.ProxyProvider;
import gearth.protocol.connection.proxy.nitro.http.NitroAuthority;
import gearth.protocol.connection.proxy.nitro.http.NitroCertificateSniffingManager;
import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxy;
import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxyServerCallback;
import gearth.protocol.connection.proxy.nitro.websocket.NitroWebsocketProxy;
@ -13,7 +15,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.atomic.AtomicBoolean;
public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCallback, StateChangeListener {
@ -32,11 +33,14 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa
private String originalCookies;
public NitroProxyProvider(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection) {
final NitroAuthority authority = new NitroAuthority();
final NitroCertificateSniffingManager certificateManager = new NitroCertificateSniffingManager(authority);
this.proxySetter = proxySetter;
this.stateSetter = stateSetter;
this.connection = connection;
this.nitroHttpProxy = new NitroHttpProxy(this);
this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this);
this.nitroHttpProxy = new NitroHttpProxy(this, certificateManager);
this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this, certificateManager);
this.abortLock = new AtomicBoolean();
}
@ -122,7 +126,7 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa
public String replaceWebsocketServer(String configUrl, String websocketUrl) {
originalWebsocketUrl = websocketUrl;
return String.format("ws://127.0.0.1:%d", websocketPort);
return String.format("wss://127.0.0.1:%d", websocketPort);
}
@Override

View File

@ -20,12 +20,31 @@ public class NitroCertificateSniffingManager implements MitmManager {
private static final boolean DEBUG = false;
private final BouncyCastleSslEngineSource sslEngineSource;
private final Authority authority;
public NitroCertificateSniffingManager(Authority authority) throws RootCertificateException {
public NitroCertificateSniffingManager(Authority authority) {
this.authority = authority;
try {
sslEngineSource = new BouncyCastleSslEngineSource(authority, true, true, null);
} catch (final Exception e) {
throw new RootCertificateException("Errors during assembling root CA.", e);
throw new RuntimeException(new RootCertificateException("Errors during assembling root CA.", e));
}
}
public Authority getAuthority() {
return authority;
}
public SSLEngine websocketSslEngine(String commonName) {
final SubjectAlternativeNameHolder san = new SubjectAlternativeNameHolder();
san.addDomainName("localhost");
san.addIpAddress("127.0.0.1");
try {
return sslEngineSource.createCertForHost(commonName, san);
} catch (Exception e) {
throw new FakeCertificateException("Failed to create WebSocket certificate", e);
}
}

View File

@ -12,8 +12,6 @@ import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import org.littleshoot.proxy.HttpProxyServer;
import org.littleshoot.proxy.impl.DefaultHttpProxyServer;
import org.littleshoot.proxy.mitm.Authority;
import org.littleshoot.proxy.mitm.RootCertificateException;
import java.io.File;
import java.io.IOException;
@ -25,20 +23,20 @@ public class NitroHttpProxy {
private static final String ADMIN_WARNING_KEY = "admin_warning_dialog";
private static final AtomicBoolean SHUTDOWN_HOOK = new AtomicBoolean();
private final Authority authority;
private final NitroOsFunctions osFunctions;
private final NitroHttpProxyServerCallback serverCallback;
private final NitroCertificateSniffingManager certificateManager;
private HttpProxyServer proxyServer = null;
public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback) {
public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback, NitroCertificateSniffingManager certificateManager) {
this.serverCallback = serverCallback;
this.authority = new NitroAuthority();
this.certificateManager = certificateManager;
this.osFunctions = NitroOsFunctionsFactory.create();
}
private boolean initializeCertificate() {
final File certificate = this.authority.aliasFile(".pem");
final File certificate = this.certificateManager.getAuthority().aliasFile(".pem");
// All good if certificate is already trusted.
if (this.osFunctions.isRootCertificateTrusted(certificate)) {
@ -80,7 +78,7 @@ public class NitroHttpProxy {
return false;
}
return this.osFunctions.installRootCertificate(this.authority.aliasFile(".pem"));
return this.osFunctions.installRootCertificate(this.certificateManager.getAuthority().aliasFile(".pem"));
}
/**
@ -100,33 +98,28 @@ public class NitroHttpProxy {
public boolean start() {
setupShutdownHook();
try {
proxyServer = DefaultHttpProxyServer.bootstrap()
.withPort(NitroConstants.HTTP_PORT)
.withManInTheMiddle(new NitroCertificateSniffingManager(authority))
.withFiltersSource(new NitroHttpProxyFilterSource(serverCallback))
.withTransparent(true)
.start();
proxyServer = DefaultHttpProxyServer.bootstrap()
.withPort(NitroConstants.HTTP_PORT)
.withManInTheMiddle(this.certificateManager)
.withFiltersSource(new NitroHttpProxyFilterSource(serverCallback))
.withTransparent(true)
.start();
if (!initializeCertificate()) {
proxyServer.stop();
if (!initializeCertificate()) {
proxyServer.stop();
System.out.println("Failed to initialize certificate");
return false;
}
if (!registerProxy()) {
proxyServer.stop();
System.out.println("Failed to register certificate");
return false;
}
return true;
} catch (RootCertificateException e) {
e.printStackTrace();
System.out.println("Failed to initialize certificate");
return false;
}
if (!registerProxy()) {
proxyServer.stop();
System.out.println("Failed to register certificate");
return false;
}
return true;
}
public void pause() {

View File

@ -16,19 +16,24 @@ import java.util.regex.Pattern;
public class NitroHttpProxyFilter extends HttpFiltersAdapter {
private static final String NitroConfigSearch = "\"socket.url\"";
private static final String NitroConfigSearch = "socket.url";
private static final String NitroClientSearch = "configurationUrls:";
private static final Pattern NitroConfigPattern = Pattern.compile("\"socket\\.url\":.?\"(wss?://.*?)\"", Pattern.MULTILINE);
private static final Pattern NitroConfigPattern = Pattern.compile("[\"']socket\\.url[\"']:(\\s+)?[\"'](wss?:.*?)[\"']", Pattern.MULTILINE);
// https://developers.cloudflare.com/fundamentals/get-started/reference/cloudflare-cookies/
private static final HashSet<String> CloudflareCookies = new HashSet<>(Arrays.asList(
"__cflb",
"__cf_bm",
"__cfseq",
"cf_ob_info",
"cf_use_ob",
"__cfwaitingroom",
"__cfruid",
"cf_clearance"
"_cfuvid",
"cf_clearance",
"cf_chl_rc_i",
"cf_chl_rc_ni",
"cf_chl_rc_m"
));
private static final String HeaderAcceptEncoding = "Accept-Encoding";
@ -95,11 +100,11 @@ public class NitroHttpProxyFilter extends HttpFiltersAdapter {
final Matcher matcher = NitroConfigPattern.matcher(responseBody);
if (matcher.find()) {
final String originalWebsocket = matcher.group(1);
final String originalWebsocket = matcher.group(2).replace("\\/", "/");
final String replacementWebsocket = callback.replaceWebsocketServer(this.url, originalWebsocket);
if (replacementWebsocket != null) {
responseBody = responseBody.replace(originalWebsocket, replacementWebsocket);
responseBody = responseBody.replace(matcher.group(2), replacementWebsocket);
responseModified = true;
}
}

View File

@ -0,0 +1,20 @@
package gearth.protocol.connection.proxy.nitro.http;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import javax.net.ssl.SSLEngine;
public class NitroSslContextFactory extends SslContextFactory.Server {
private final NitroCertificateSniffingManager certificateManager;
public NitroSslContextFactory(NitroCertificateSniffingManager certificateManager) {
this.certificateManager = certificateManager;
}
@Override
public SSLEngine newSSLEngine(String host, int port) {
System.out.printf("[NitroSslContextFactory] Creating SSLEngine for %s:%d%n", host, port);
return certificateManager.websocketSslEngine(host);
}
}

View File

@ -1,6 +1,7 @@
package gearth.protocol.connection.proxy.nitro.os;
import gearth.misc.OSValidator;
import gearth.protocol.connection.proxy.nitro.os.macos.NitroMacOS;
import gearth.protocol.connection.proxy.nitro.os.windows.NitroWindows;
import org.apache.commons.lang3.NotImplementedException;
@ -15,7 +16,10 @@ public final class NitroOsFunctionsFactory {
throw new NotImplementedException("unix nitro is not implemented yet");
}
throw new NotImplementedException("macOS nitro is not implemented yet");
}
if (OSValidator.isMac()) {
return new NitroMacOS();
}
throw new NotImplementedException("unsupported operating system");
}
}

View File

@ -0,0 +1,96 @@
package gearth.protocol.connection.proxy.nitro.os.macos;
import gearth.misc.RuntimeUtil;
import gearth.protocol.connection.proxy.nitro.os.NitroOsFunctions;
import java.io.File;
import java.io.IOException;
public class NitroMacOS implements NitroOsFunctions {
/**
* Semicolon separated hosts to ignore for proxying.
*/
private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;";
/**
* Checks if the certificate is trusted by the local machine.
* @param certificate Absolute path to the certificate.
* @return true if trusted
*/
@Override
public boolean isRootCertificateTrusted(File certificate) {
try {
final String output = RuntimeUtil.getCommandOutput(new String[] {"sh", "-c", "security verify-cert -c \"" + certificate.getAbsolutePath() + "\""});
return !output.contains("CSSMERR_TP_NOT_TRUSTED");
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean installRootCertificate(File certificate) {
final String certificatePath = certificate.getAbsolutePath();
try {
// Install the certificate
Process process = new ProcessBuilder("sh", "-c", "sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain \"" + certificatePath + "\"")
.inheritIO()
.start();
// Wait for the process to complete
int exitCode = process.waitFor();
if (exitCode != 0) {
System.out.printf("security add-trusted-cert command exited with exit code %d%n", exitCode);
return false;
}
return true;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean registerSystemProxy(String host, int port) {
try {
final String[] ignoreHosts = PROXY_IGNORE.split(";");
// Enable proxy
Runtime.getRuntime().exec("networksetup -setwebproxy Wi-Fi " + host + " " + port);
Runtime.getRuntime().exec("networksetup -setsecurewebproxy Wi-Fi " + host + " " + port);
// Set proxy bypass domains
for (String ignoreHost : ignoreHosts) {
Runtime.getRuntime().exec("networksetup -setproxybypassdomains Wi-Fi " + ignoreHost);
}
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean unregisterSystemProxy() {
try {
// Disable proxy
Runtime.getRuntime().exec("networksetup -setwebproxystate Wi-Fi off");
Runtime.getRuntime().exec("networksetup -setsecurewebproxystate Wi-Fi off");
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}

View File

@ -15,7 +15,8 @@ public class NitroWindows implements NitroOsFunctions {
/**
* Semicolon separated hosts to ignore for proxying.
*/
private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;";
// habba.io;
private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;challenges.cloudflare.com;";
/**
* Checks if the certificate is trusted by the local machine.

View File

@ -1,6 +1,6 @@
package gearth.protocol.connection.proxy.nitro.websocket;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.Session;
public interface NitroSession {

View File

@ -2,8 +2,11 @@ package gearth.protocol.connection.proxy.nitro.websocket;
import gearth.protocol.HConnection;
import gearth.protocol.HMessage;
import gearth.protocol.StateChangeListener;
import gearth.protocol.connection.*;
import gearth.protocol.connection.proxy.nitro.NitroConnectionState;
import gearth.protocol.connection.proxy.nitro.NitroConstants;
import gearth.protocol.connection.proxy.nitro.NitroPacketQueue;
import gearth.protocol.connection.proxy.nitro.NitroProxyProvider;
import gearth.protocol.packethandler.nitro.NitroPacketHandler;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
@ -25,22 +28,24 @@ public class NitroWebsocketClient implements NitroSession {
private static final Logger logger = LoggerFactory.getLogger(NitroWebsocketClient.class);
private final HProxySetter proxySetter;
private final HStateSetter stateSetter;
private final HConnection connection;
private final NitroConnectionState state;
private final NitroProxyProvider proxyProvider;
private final NitroWebsocketServer server;
private final NitroPacketHandler packetHandler;
private final NitroPacketQueue packetQueue;
private final AtomicBoolean shutdownLock;
private JsrSession activeSession = null;
public NitroWebsocketClient(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection, NitroProxyProvider proxyProvider) {
this.proxySetter = proxySetter;
this.stateSetter = stateSetter;
this.connection = connection;
this.state = new NitroConnectionState(stateSetter);
this.proxyProvider = proxyProvider;
this.server = new NitroWebsocketServer(connection, this);
this.server = new NitroWebsocketServer(connection, this, this.state);
this.packetHandler = new NitroPacketHandler(HMessage.Direction.TOSERVER, server, connection.getExtensionHandler(), connection.getTrafficObservables());
this.packetQueue = new NitroPacketQueue(this.packetHandler);
this.shutdownLock = new AtomicBoolean();
}
@ -48,8 +53,27 @@ public class NitroWebsocketClient implements NitroSession {
public void onOpen(Session session) throws Exception {
logger.info("WebSocket connection accepted");
// Setup state change listener
connection.getStateObservable().addListener(new StateChangeListener() {
@Override
public void stateChanged(HState oldState, HState newState) {
// Clean up when we don't need it anymore.
if ((oldState == HState.WAITING_FOR_CLIENT || newState == HState.NOT_CONNECTED) || newState == HState.ABORTING) {
connection.getStateObservable().removeListener(this);
}
// Process queue when connected.
try {
packetQueue.flush();
} catch (IOException e) {
logger.error("Failed to flush packet queue in state change listener", e);
}
}
});
activeSession = (JsrSession) session;
activeSession.setMaxBinaryMessageBufferSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
activeSession.getPolicy().setMaxBinaryMessageSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
activeSession.getPolicy().setMaxTextMessageSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
// Set proper headers to spoof being a real client.
final Map<String, List<String>> headers = new HashMap<>(activeSession.getUpgradeRequest().getHeaders());
@ -71,12 +95,21 @@ public class NitroWebsocketClient implements NitroSession {
);
proxySetter.setProxy(proxy);
stateSetter.setState(HState.CONNECTED);
state.setConnected(HMessage.Direction.TOSERVER);
}
@OnMessage
public void onMessage(byte[] b, Session session) throws IOException {
packetHandler.act(b);
logger.debug("Received packet from browser");
// Enqueue all packets we receive to ensure we preserve correct packet order.
packetQueue.enqueue(b);
// Flush everything if we are connected.
// We also flush when connection state changes to connected.
if (state.isConnected()) {
packetQueue.flush();
}
}
@OnClose
@ -94,7 +127,7 @@ public class NitroWebsocketClient implements NitroSession {
}
@Override
public Session getSession() {
public org.eclipse.jetty.websocket.api.Session getSession() {
return activeSession;
}
@ -132,7 +165,7 @@ public class NitroWebsocketClient implements NitroSession {
// Reset program state.
proxySetter.setProxy(null);
stateSetter.setState(HState.ABORTING);
state.setAborting();
}
}
}

View File

@ -4,7 +4,8 @@ import gearth.protocol.HConnection;
import gearth.protocol.connection.HProxySetter;
import gearth.protocol.connection.HStateSetter;
import gearth.protocol.connection.proxy.nitro.NitroProxyProvider;
import org.eclipse.jetty.server.Connector;
import gearth.protocol.connection.proxy.nitro.http.NitroCertificateSniffingManager;
import gearth.protocol.connection.proxy.nitro.http.NitroSslContextFactory;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
@ -21,19 +22,37 @@ public class NitroWebsocketProxy {
private final HStateSetter stateSetter;
private final HConnection connection;
private final NitroProxyProvider proxyProvider;
private final NitroCertificateSniffingManager certificateManager;
private final Server server;
private final int serverPort;
public NitroWebsocketProxy(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection, NitroProxyProvider proxyProvider) {
public NitroWebsocketProxy(HProxySetter proxySetter,
HStateSetter stateSetter,
HConnection connection,
NitroProxyProvider proxyProvider,
NitroCertificateSniffingManager certificateManager) {
this.proxySetter = proxySetter;
this.stateSetter = stateSetter;
this.connection = connection;
this.proxyProvider = proxyProvider;
this.server = new Server(0);
this.certificateManager = certificateManager;
this.server = new Server();
this.serverPort = 0;
}
public boolean start() {
try {
// Configure SSL.
final NitroSslContextFactory sslContextFactory = new NitroSslContextFactory(this.certificateManager);
final ServerConnector sslConnector = new ServerConnector(server, sslContextFactory);
sslConnector.setPort(this.serverPort);
// Add SSL to the server.
server.addConnector(sslConnector);
// Configure the WebSocket.
final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");

View File

@ -2,20 +2,34 @@ package gearth.protocol.connection.proxy.nitro.websocket;
import gearth.protocol.HConnection;
import gearth.protocol.HMessage;
import gearth.protocol.connection.proxy.nitro.NitroConnectionState;
import gearth.protocol.connection.proxy.nitro.NitroConstants;
import gearth.protocol.packethandler.PacketHandler;
import gearth.protocol.packethandler.nitro.NitroPacketHandler;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.jsr356.JsrExtension;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.*;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.URI;
import java.util.*;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
public class NitroWebsocketServer extends Endpoint implements NitroSession {
public class NitroWebsocketServer implements WebSocketListener, NitroSession {
private static final Logger logger = LoggerFactory.getLogger(NitroWebsocketServer.class);
@ -25,78 +39,56 @@ public class NitroWebsocketServer extends Endpoint implements NitroSession {
"Sec-WebSocket-Version",
"Host",
"Connection",
"Upgrade"
"Upgrade",
"User-Agent", // Added by default
"Accept-Encoding", // Added by default
"Cache-Control", // Added by default
"Pragma" // Added by default
));
private final PacketHandler packetHandler;
private final NitroWebsocketClient client;
private final NitroConnectionState state;
private Session activeSession = null;
public NitroWebsocketServer(HConnection connection, NitroWebsocketClient client) {
public NitroWebsocketServer(HConnection connection, NitroWebsocketClient client, NitroConnectionState state) {
this.client = client;
this.state = state;
this.packetHandler = new NitroPacketHandler(HMessage.Direction.TOCLIENT, client, connection.getExtensionHandler(), connection.getTrafficObservables());
}
public void connect(String websocketUrl, Map<String, List<String>> clientHeaders) throws IOException {
try {
logger.info("Connecting to origin websocket at {}", websocketUrl);
logger.info("Building origin websocket connection ({})", websocketUrl);
ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create();
final WebSocketClient client = createWebSocketClient();
builder.extensions(Collections.singletonList(new JsrExtension(new ExtensionConfig("permessage-deflate;client_max_window_bits"))));
final ClientUpgradeRequest request = new ClientUpgradeRequest();
builder.configurator(new ClientEndpointConfig.Configurator() {
@Override
public void beforeRequest(Map<String, List<String>> headers) {
clientHeaders.forEach((key, value) -> {
if (SKIP_HEADERS.contains(key)) {
return;
}
request.addExtensions("permessage-deflate");
headers.remove(key);
headers.put(key, value);
});
clientHeaders.forEach((key, value) -> {
if (SKIP_HEADERS.contains(key)) {
return;
}
request.setHeader(key, value);
});
ClientEndpointConfig config = builder.build();
if (clientHeaders.containsKey("User-Agent")) {
final String realUserAgent = clientHeaders.get(HttpHeader.USER_AGENT.toString()).get(0);
final HttpField clientUserAgent = new HttpField(HttpHeader.USER_AGENT, realUserAgent);
ContainerProvider.getWebSocketContainer().connectToServer(this, config, URI.create(websocketUrl));
logger.info("Connected to origin websocket");
} catch (DeploymentException e) {
throw new IOException("Failed to deploy websocket client", e);
}
}
@Override
public void onOpen(Session session, EndpointConfig config) {
this.activeSession = session;
this.activeSession.setMaxBinaryMessageBufferSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
this.activeSession.addMessageHandler(new MessageHandler.Whole<byte[]>() {
@Override
public void onMessage(byte[] message) {
try {
packetHandler.act(message);
} catch (IOException e) {
e.printStackTrace();
}
client.getHttpClient().setUserAgentField(clientUserAgent);
}
});
}
@Override
public void onClose(Session session, CloseReason closeReason) {
// Hotel closed connection.
client.shutdownProxy();
}
logger.info("Connecting to origin websocket at {}", websocketUrl);
@Override
public void onError(Session session, Throwable throwable) {
throwable.printStackTrace();
// Shutdown.
client.shutdownProxy();
client.start();
client.connect(this, URI.create(websocketUrl), request);
} catch (Exception e) {
throw new IOException("Failed to start websocket client to origin " + websocketUrl, e);
}
}
@Override
@ -118,10 +110,87 @@ public class NitroWebsocketServer extends Endpoint implements NitroSession {
try {
activeSession.close();
} catch (IOException e) {
} catch (Exception e) {
e.printStackTrace();
} finally {
activeSession = null;
}
}
@Override
public void onWebSocketBinary(byte[] bytes, int i, int i1) {
try {
packetHandler.act(bytes);
} catch (IOException e) {
logger.error("Failed to handle packet", e);
}
}
@Override
public void onWebSocketText(String s) {
logger.warn("Received text message from hotel");
}
@Override
public void onWebSocketClose(int i, String s) {
// Hotel closed connection.
client.shutdownProxy();
}
@Override
public void onWebSocketConnect(org.eclipse.jetty.websocket.api.Session session) {
activeSession = session;
state.setConnected(HMessage.Direction.TOCLIENT);
}
@Override
public void onWebSocketError(Throwable throwable) {
throwable.printStackTrace();
// Shutdown.
client.shutdownProxy();
}
private SSLContext createSSLContext() {
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
try {
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
return sslContext;
} catch (Exception e) {
throw new RuntimeException("Failed to setup ssl context", e);
}
}
private HttpClient createHttpClient() {
final SslContextFactory.Client factory = new SslContextFactory.Client();
factory.setSslContext(createSSLContext());
return new HttpClient(factory);
}
private WebSocketClient createWebSocketClient() {
final WebSocketClient client = new WebSocketClient(createHttpClient());
client.getPolicy().setMaxBinaryMessageSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
client.getPolicy().setMaxTextMessageSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
return client;
}
}

View File

@ -134,6 +134,10 @@ public class MacOsHabboClient extends HabboClient {
private static byte[] hexStringToByteArray(String s) {
int len = s.length();
if (len % 2 == 1) {
s = "0" + s;
len += 1;
}
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)

View File

@ -6,13 +6,17 @@ import gearth.protocol.connection.proxy.nitro.websocket.NitroSession;
import gearth.protocol.packethandler.PacketHandler;
import gearth.protocol.packethandler.PayloadBuffer;
import gearth.services.extension_handler.ExtensionHandler;
import org.eclipse.jetty.websocket.api.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.Session;
import java.io.IOException;
import java.nio.ByteBuffer;
public class NitroPacketHandler extends PacketHandler {
private static final Logger logger = LoggerFactory.getLogger(NitroPacketHandler.class);
private final HMessage.Direction direction;
private final NitroSession session;
private final PayloadBuffer payloadBuffer;
@ -31,6 +35,7 @@ public class NitroPacketHandler extends PacketHandler {
final Session localSession = session.getSession();
if (localSession == null) {
logger.warn("Discarding {} bytes because the session for direction {} was null", buffer.length, this.direction);
return false;
}
@ -39,7 +44,13 @@ public class NitroPacketHandler extends PacketHandler {
buffer = buffer.clone();
}
localSession.getAsyncRemote().sendBinary(ByteBuffer.wrap(buffer));
try {
localSession.getRemote().sendBytes(ByteBuffer.wrap(buffer));
} catch (IOException e) {
logger.error("Error sending packet to nitro client", e);
return false;
}
return true;
}

View File

@ -357,7 +357,7 @@ public class UiLoggerController implements Initializable {
public void updateLoggerInfo() {
Platform.runLater(() -> {
viewIncoming.setKey(1, "ext.logger.state." + (chkViewIncoming.isSelected() ? "true" : "false"));
viewIncoming.setKey(1, "ext.logger.state." + (chkViewOutgoing.isSelected() ? "true" : "false"));
viewOutgoing.setKey(1, "ext.logger.state." + (chkViewOutgoing.isSelected() ? "true" : "false"));
autoScroll.setKey(1, "ext.logger.state." + (chkAutoscroll.isSelected() ? "true" : "false"));
filtered.setFormat("%s: " + filteredAmount);

View File

@ -0,0 +1,105 @@
package gearth.ui;
import gearth.GEarth;
import gearth.services.extension_handler.extensions.GEarthExtension;
import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducerFactory;
import gearth.services.extension_handler.extensions.implementations.network.NetworkExtensionServer;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
/**
* Adds a {@link TrayIcon} to the {@link SystemTray} for this G-Earth instance.
*
* @author Dorving
*/
public final class GEarthTrayIcon {
private static final String TO_FRONT_LABEL = "To front";
private static final String TO_BACK_LABEL = "To back";
private static PopupMenu menu;
public static void updateOrCreate(Image image) {
if (!SystemTray.isSupported())
return;
final NetworkExtensionServer server = ExtensionProducerFactory.getExtensionServer();
final BufferedImage awtImage = SwingFXUtils.fromFXImage(image, null);
final String appTitle = "G-Earth " + GEarth.version + " (" + server.getPort() + ")";
final Optional<TrayIcon> trayIcon = Stream.of(SystemTray.getSystemTray().getTrayIcons())
.filter(other -> Objects.equals(other.getToolTip(), appTitle))
.findFirst();
if (trayIcon.isPresent()) {
EventQueue.invokeLater(() -> trayIcon.get().setImage(awtImage));
} else {
menu = new PopupMenu();
menu.add(createToFrontOrBackMenuItem());
menu.addSeparator();
menu.addSeparator();
menu.add(createInstallMenuItem());
try {
SystemTray.getSystemTray().add(new TrayIcon(awtImage, appTitle, menu));
} catch (AWTException e) {
e.printStackTrace();
menu = null;
}
}
}
private static MenuItem createToFrontOrBackMenuItem() {
final MenuItem showMenuItem = new MenuItem(TO_FRONT_LABEL);
showMenuItem.addActionListener(e -> {
if (Objects.equals(showMenuItem.getLabel(), TO_FRONT_LABEL)) {
showMenuItem.setLabel(TO_BACK_LABEL);
Platform.runLater(() -> GEarth.main.getController().getStage().toFront());
} else {
showMenuItem.setLabel(TO_FRONT_LABEL);
Platform.runLater(() -> GEarth.main.getController().getStage().toBack());
}
});
return showMenuItem;
}
private static MenuItem createInstallMenuItem() {
final MenuItem showMenuItem = new MenuItem("Install Extension...");
showMenuItem.addActionListener(e ->
Optional.ofNullable(GEarth.main.getController())
.map(c -> c.extensionsController)
.ifPresent(c -> Platform.runLater(() -> {
final Stage stage = c.parentController.getStage();
final boolean isOnTop = stage.isAlwaysOnTop();
stage.setAlwaysOnTop(true); // bit of a hack to force stage to front
c.installBtnClicked(null);
stage.setAlwaysOnTop(isOnTop);
})));
return showMenuItem;
}
/**
* Adds the argued extension as a menu item to {@link #menu}.
*
* @param extension the {@link GEarthExtension} to add to the {@link #menu}.
*/
public static void addExtension(GEarthExtension extension) {
if (menu == null)
return;
final MenuItem menuItem = new MenuItem("Show "+extension.getTitle());
EventQueue.invokeLater(() -> menu.insert(menuItem, 2));
menuItem
.addActionListener(e -> Platform.runLater(() -> extension.getClickedObservable().fireEvent()));
extension.getDeletedObservable()
.addListener(() -> EventQueue.invokeLater(() -> menu.remove(menuItem)));
}
}

View File

@ -8,6 +8,7 @@ import gearth.services.extension_handler.extensions.implementations.network.exec
import gearth.services.extension_handler.extensions.implementations.network.executer.ExtensionRunner;
import gearth.services.extension_handler.extensions.implementations.network.executer.ExtensionRunnerFactory;
import gearth.services.g_python.GPythonShell;
import gearth.ui.GEarthTrayIcon;
import gearth.ui.SubForm;
import gearth.ui.subforms.extensions.logger.ExtensionLogger;
import gearth.ui.translations.LanguageBundle;
@ -18,6 +19,7 @@ import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
@ -54,9 +56,10 @@ public class ExtensionsController extends SubForm {
protected void onParentSet() {
ExtensionItemContainerProducer producer = new ExtensionItemContainerProducer(extensioncontainer, scroller);
extensionHandler = new ExtensionHandler(getHConnection());
extensionHandler.getObservable().addListener((e -> {
Platform.runLater(() -> producer.extensionConnected(e));
}));
extensionHandler.getObservable().addListener((e -> Platform.runLater(() -> {
producer.extensionConnected(e);
GEarthTrayIcon.addExtension(e);
})));
//noinspection OptionalGetWithoutIsPresent
networkExtensionsProducer

View File

@ -147,7 +147,7 @@ alert.outdated.content.newversion=Uma nova versão do G-Earth foi encontrada
alert.outdated.content.update=Atualizar para a versão mais recente
### Alert - Invalid connection
alert.invalidconnection.content=Você entrou com inforções de conexão inválidas, o G-Earth não conseguiu se conectar
alert.invalidconnection.content=Você entrou com informações de conexão inválidas, o G-Earth não conseguiu se conectar
### Alert - Nitro root certificate
alert.rootcertificate.title=Instalação de certificado do root
@ -169,7 +169,7 @@ alert.somethingwentwrong.content=Algo inesperado aconteceu!\n\
para nossa página de Solução de problemas para resolver o ocorrido:
### Alert - Allow extension connection
alert.extconnection.content=Exntesão "%s" tentou se conectar mas é desconcida pelo G-Earth,\n\
alert.extconnection.content=Extensão "%s" tentou se conectar mas é desconhecida pelo G-Earth,\n\
aceitar essa conexão?
### Alert - G-Python error
@ -183,8 +183,8 @@ alert.gpythonerror.content=Algo inesperado aconteceu executando o terminal G-Pyt
## Internal extension
### Internal extension - G-ExtensionStore
ext.store.elapsedtime.second.single=secondo
ext.store.elapsedtime.second.multiple=secondos
ext.store.elapsedtime.second.single=segundo
ext.store.elapsedtime.second.multiple=segundos
ext.store.elapsedtime.minute.single=minuto
ext.store.elapsedtime.minute.multiple=minutos
ext.store.elapsedtime.hour.single=hora
@ -219,7 +219,7 @@ ext.store.extension.details.screenshot=Captura de tela
ext.store.extension.author.reputation=reputação
ext.store.extension.author.releases=lançamentos
ext.store.extension.warning.requirement=Aviso: o framework requer --url:instalações adicionais-
ext.store.extension.warning.requirement=Aviso: o framework requer --url:additional installations-
! IMPORTANT: the previous line has to end with the --url component like the english version
ext.store.extension.warning.unstable=Aviso: esta extensão foi marcada como instável!
@ -272,17 +272,17 @@ ext.store.categories.title=Categorias
ext.store.categories.description=Explore os diferentes tipos de extensões que o G-Earth tem a oferecer
ext.store.categories.contenttitle=Categorias
ext.store.fail.unzip=Error while unzipping
ext.store.fail.invalidurl=Invalid extension URL
ext.store.fail.notavailable=Extension not available in repository
ext.store.fail.alreadyexists=Something went wrong creating the extension directory, does the extension already exist?
ext.store.fail.notfound=Extension wasn't found
ext.store.fail.uninstall=Something went wrong with uninstalling the extension, make sure to disconnect the extension if it was still running.
ext.store.fail.unzip=Erro ao descompactar
ext.store.fail.invalidurl=URL de extensão inválida
ext.store.fail.notavailable=Extensão não disponível no repositório
ext.store.fail.alreadyexists=Algo deu errado ao criar o diretório de extensão, a extensão já existe?
ext.store.fail.notfound=Extensão não foi encontrada
ext.store.fail.uninstall=Algo deu errado ao desinstalar a extensão, certifique-se de desconectar a extensão se ela ainda estiver em execução.
ext.store.ordering.rating=Rating
ext.store.ordering.alphabetical=Alphabetical
ext.store.ordering.lastupdated=Last updated
ext.store.ordering.newreleases=New releases
ext.store.ordering.rating=Avaliação
ext.store.ordering.alphabetical=Alfabético
ext.store.ordering.lastupdated=Última atualização
ext.store.ordering.newreleases=Novos lançamentos
### Internal extension - Logger
ext.logger.menu.window=Janela
@ -307,7 +307,7 @@ ext.logger.menu.packets.displaydetails.byterep.legacy=Legado
ext.logger.menu.packets.displaydetails.byterep.hexdump=Hexdump
ext.logger.menu.packets.displaydetails.byterep.rawhex=Hex cru
ext.logger.menu.packets.displaydetails.byterep.none=Nenhum
ext.logger.menu.packets.displaydetails.message=Menssagem
ext.logger.menu.packets.displaydetails.message=Mensagem
ext.logger.menu.packets.displaydetails.message.name=Nome
ext.logger.menu.packets.displaydetails.message.hash=Hash
ext.logger.menu.packets.displaydetails.message.id=Id

View File

@ -36,10 +36,10 @@ Name | Language | Developers | Github
--- | --- | --- | --- |
G-Earth (Native) | Java | sirjonasxx | https://github.com/sirjonasxx/G-Earth
G-Python<sup>1</sup> | Python | sirjonasxx | https://github.com/sirjonasxx/G-Python
Geode | C# & Visual Basic | ArachisH, LilithRainbows | https://github.com/LilithRainbows/Geode
Xabbo | C# | b7 | https://github.com/b7c/Xabbo.GEarth
Xabbo scripter<sup>2</sup> | C# | b7 | https://github.com/b7c/Xabbo.Scripter
G-Node | Node.js | WiredSpast | https://github.com/WiredSpast/G-Node
G-Rust | Rust | WiredSpast | https://github.com/WiredSpast/G-Rust
GProgrammer<sup>3</sup> | Javascript | at15four2020 | https://github.com/at15four2020/GProgrammer/wiki
<sub>1: built-in in G-Earth through the [live scripting console](https://github.com/sirjonasxx/G-Earth/wiki/G-Python-qtConsole) </sub>

View File

@ -1,24 +1,89 @@
**NOTE: Currently supported browsers: ONLY Firefox and Chromium, works on Habbo AIR too**
In order to run G-Earth on macOs, you'll need to sign G-Mem (our memory searcher). This wiki page will cover that process.
# MacOS Installation Guide
First we'll create a certificate in order to sign it:
1. Open the Keychain Access application (You may find it inside Utilities).£
2. Select Certificate Assistant -> Create a Certificate.<br>
![](https://i.imgur.com/G6SS6ac.png)
3. Choose a name for the certificate (I'll use "gmem-cert") and set "Certificate Type" to "Code Signing". Also select "Let me override defaults" option.<br>
![](https://i.imgur.com/CAUI5Xi.png)
4. Click on "Continue" until "Specify a Location For The Certificate" appears, then set "Keychain" to "System".<br>
![](https://i.imgur.com/HwLDtmE.png)
5. Continue, the certificate will be created then.<br>
![](https://i.imgur.com/gYiKmZA.png)
G-Earth depends on an application named [G-Mem](https://github.com/sirjonasxx/G-Mem),
this application scans the memory contents of the Habbo client applicaton and extracts a cipher key
that is used to decrypt packets coming from the Habbo server.
Once created, we are able to codesign gmem from Terminal<br>
`codesign -fs "gmem-cert" <G-Earth_Path>/G-Mem`
There is a few steps u have to complete in order to get it to work on MacOS.
![](https://i.imgur.com/xkryoJz.png)
## Code-Sign G-Mem file and make it executable
Now you're ready to open G-Earth from Terminal<br>
`sudo java -jar G-Earth.jar`
### Certificate Creation
1. Open `Keychain Access` (press `⌘ + Enter`, type `KeyChain Access` to open it from spotlight)
2. Navigate from the top menu to `Keychain Access > Certificate Assistant > Create Certificate...`
![Screenshot 2023-03-30 at 14 36 47](https://user-images.githubusercontent.com/102377087/228837955-81182786-ac47-46e5-a5e2-1ca2e257751f.png)
3. In the `Create Your Certificate` do the following:
* Set `Name:` to `gmem-cert`
* Set `Cerificate Type:` to `Code Signing`
* Toggle the `Let me override default` button
![Screenshot 2023-03-30 at 14 40 28](https://user-images.githubusercontent.com/102377087/228838867-57e465bc-5b83-4b1a-a8cc-3dd6d1e95353.png)
5. Press `Continue` until you reach the `Specify a Location For The Certificate`, now do the following:
* Set `Keychain:` to `System`
![Screenshot 2023-03-30 at 14 42 50](https://user-images.githubusercontent.com/102377087/228839468-982365d9-925c-44cf-a87d-fc6c268d05c8.png)
6. Enter your login credentials when prompted and press `Done`
### Signing of G-Mem
1. Open `Terminal` (press `⌘ + Enter`, type `Terminal` to open it from spotlight)
2. Type `codesign -fs "gmem-cert" ` (do not press enter yet)
3. Drag the `G-Mem` file into your terminal window *(this will append the path to the terminal)*
Your terminal window should now resemble the following:
![Screenshot 2023-03-30 at 14 49 28](https://user-images.githubusercontent.com/102377087/228841126-77b0184b-4c7d-44e0-9f7c-56103a957a81.png)
4. Now press enter and enter your login credentials when prompted.
### Making G-Mem executable
1. Open `Terminal` (press `⌘ + Enter`, type `Terminal` to open it from spotlight)
2. Type `chmod 755 ` (do not press enter yet)
3. Drag the `G-Mem` file into your terminal window *(this will append the path to the terminal)*
Your terminal window should now resemble the following:
![Screenshot 2023-03-30 at 14 52 29](https://user-images.githubusercontent.com/102377087/228841918-3205014b-5de8-431d-ae4d-d10b8ceeed03.png)
4. Now press enter and verify the `Kind` of the `G-Mem` file is now `Unix Executable File`
![Screenshot 2023-03-30 at 14 54 15](https://user-images.githubusercontent.com/102377087/228842389-78ea857e-3414-43d0-8270-91f8185ab57f.png)
## Disabling SIP
Modern machines running MacOS have a security feature that shields of the memory of processes from other processes.
Depending on your machine you may have to disable SIP.
**For M1 macs it is required to disable SIP.**
### :warning: CAUTION :warning:
Turning off SIP allows any program with sudo privileges to modify memory contents of other processes. If you use pirated software, or other unverified apps, DO NOT DO THIS for your own safety! See the following stackoverflow post for some more info: https://apple.stackexchange.com/a/412281.
A guide for disabling SIP can be found here: https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection
## Launching G-Earth
1. Open `Terminal` (press `⌘ + Enter`, type `Terminal` to open it from spotlight)
2. Type `sudo java -jar `
3. Drag the `G-Earth.jar` file into your terminal window *(this will append the path to the terminal)*
Your terminal window should now resemble the following:
![Screenshot 2023-03-30 at 15 00 59](https://user-images.githubusercontent.com/102377087/228843994-f7713373-9f19-49b0-b7e7-0645a16c4fce.png)
5. Press enter and fill in your password if prompted
## Troubleshooting
### 🚫 Apple cant check app for malicious software
In this case, you can grant an exception for a blocked app by clicking the `Open Anyway` button in Privacy & Security settings.
This button is available for about an hour after you try to open the app. Where it appears exactly depends on the OS version u are running,
more details about it can be found here: https://support.apple.com/en-gb/guide/mac-help/mchleab3a043/mac
### Other
If you experience any other issues and the [Troubleshooting Page](https://github.com/sirjonasxx/G-Earth/wiki/Troubleshooting) doesn't help,
it might be useful to have a look at the following issues: [#67](../issues/67) [#10](../issues/10)
If you experience any other issues and the troubleshooting page doesn't help, it might be useful to have a look at the following issues: [#67](../issues/67) [#10](../issues/10)