Merge branch 'release/5.0.1'

This commit is contained in:
Gravit 2019-05-23 06:14:11 +07:00
commit e55200f5eb
No known key found for this signature in database
GPG key ID: 061981E1E85D3216
39 changed files with 711 additions and 262 deletions

View file

@ -24,7 +24,7 @@ build:
- ./gradlew assemble - ./gradlew assemble
artifacts: artifacts:
paths: paths:
- LaunchServer/build/libs/*.jar - LaunchServer/build/libs/*
- ServerWrapper/build/libs/*.jar - ServerWrapper/build/libs/*.jar
expire_in: 1 week expire_in: 1 week

View file

@ -263,9 +263,15 @@ public static class ExeConf {
public String txtFileVersion; public String txtFileVersion;
public String txtProductVersion; public String txtProductVersion;
} }
public static class NettyUpdatesBind
{
public String url;
public boolean zip;
}
public class LauncherConf { public class LauncherConf {
public String guardType; public String guardType;
public boolean attachLibraryBeforeProGuard;
} }
public class NettyConfig { public class NettyConfig {
@ -276,7 +282,7 @@ public class NettyConfig {
public String downloadURL; public String downloadURL;
public String launcherEXEURL; public String launcherEXEURL;
public String address; public String address;
public Map<String, String> bindings = new HashMap<>(); public Map<String, NettyUpdatesBind> bindings = new HashMap<>();
public NettyPerformanceConfig performance; public NettyPerformanceConfig performance;
public NettyBindAddress[] binds; public NettyBindAddress[] binds;
public LogLevel logLevel = LogLevel.DEBUG; public LogLevel logLevel = LogLevel.DEBUG;

View file

@ -27,6 +27,7 @@ public class MysqlHWIDHandler extends HWIDHandler {
private String hwidFieldHWDiskSerial; private String hwidFieldHWDiskSerial;
private String hwidFieldProcessorID; private String hwidFieldProcessorID;
private String hwidFieldBanned; private String hwidFieldBanned;
private String hwidFieldMAC;
private String queryHwids; private String queryHwids;
private String[] paramsHwids; private String[] paramsHwids;
@ -37,7 +38,7 @@ public class MysqlHWIDHandler extends HWIDHandler {
private String banMessage; private String banMessage;
private boolean compareMode = false; private boolean compareMode = false;
//Using queryHWID "queryHwids": "SELECT * FROM `users_hwids` WHERE `totalMemory` = ? or `serialNumber` = ? or `HWDiskSerial` = ? or `processorID` = ?" //Using queryHWID "queryHwids": "SELECT * FROM `users_hwids` WHERE `totalMemory` = ? or `serialNumber` = ? or `HWDiskSerial` = ? or `processorID` = ? or `MACAddr` = ?"
private int compare = 50; //При наборе схожести в 50 очков private int compare = 50; //При наборе схожести в 50 очков
private boolean oneCompareMode = false; private boolean oneCompareMode = false;
@ -51,6 +52,7 @@ public class MysqlHWIDHandler extends HWIDHandler {
`serialNumber` varchar(64) NOT NULL, `serialNumber` varchar(64) NOT NULL,
`HWDiskSerial` varchar(64) NOT NULL, `HWDiskSerial` varchar(64) NOT NULL,
`processorID` varchar(64) NOT NULL, `processorID` varchar(64) NOT NULL,
`MACAddr` varchar(64) NOT NULL,
`isBanned` tinyint(1) NOT NULL DEFAULT '0' `isBanned` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@ -90,7 +92,7 @@ public void check0(HWID hwid, String username) throws HWIDException {
public void onUpdateInfo(OshiHWID hwid, String username, Connection c) throws HWIDException { public void onUpdateInfo(OshiHWID hwid, String username, Connection c) throws HWIDException {
try (PreparedStatement a = c.prepareStatement(queryHwids)) { try (PreparedStatement a = c.prepareStatement(queryHwids)) {
String[] replaceParams = {"totalMemory", String.valueOf(hwid.totalMemory), "serialNumber", hwid.serialNumber, "HWDiskSerial", hwid.HWDiskSerial, "processorID", hwid.processorID}; String[] replaceParams = {"totalMemory", String.valueOf(hwid.totalMemory), "serialNumber", hwid.serialNumber, "HWDiskSerial", hwid.HWDiskSerial, "processorID", hwid.processorID, "MAC", hwid.macAddr};
for (int i = 0; i < paramsHwids.length; i++) { for (int i = 0; i < paramsHwids.length; i++) {
a.setString(i + 1, CommonHelper.replace(paramsHwids[i], replaceParams)); a.setString(i + 1, CommonHelper.replace(paramsHwids[i], replaceParams));
} }
@ -113,12 +115,13 @@ public void onUpdateInfo(OshiHWID hwid, String username, Connection c) throws HW
throw new HWIDException(banMessage); throw new HWIDException(banMessage);
} }
} else { } else {
ps = c.prepareStatement(String.format("INSERT INTO `%s` (`%s`, `%s`, `%s`, `%s`) VALUES (?, ?, ?, ?);", ps = c.prepareStatement(String.format("INSERT INTO `%s` (`%s`, `%s`, `%s`, `%s`, `%s`) VALUES (?, ?, ?, ?, ?);",
tableHwids, hwidFieldTotalMemory, hwidFieldSerialNumber, hwidFieldHWDiskSerial, hwidFieldProcessorID)); tableHwids, hwidFieldTotalMemory, hwidFieldSerialNumber, hwidFieldHWDiskSerial, hwidFieldProcessorID, hwidFieldMAC));
ps.setString(1, String.valueOf(hwid.totalMemory)); ps.setString(1, String.valueOf(hwid.totalMemory));
ps.setString(2, hwid.serialNumber); ps.setString(2, hwid.serialNumber);
ps.setString(3, hwid.HWDiskSerial); ps.setString(3, hwid.HWDiskSerial);
ps.setString(4, hwid.processorID); ps.setString(4, hwid.processorID);
ps.setString(5, hwid.macAddr);
ps.setQueryTimeout(MySQLSourceConfig.TIMEOUT); ps.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
ps.executeUpdate(); ps.executeUpdate();
@ -135,7 +138,7 @@ public void onUpdateInfo(OshiHWID hwid, String username, Connection c) throws HW
public void onCheckInfo(OshiHWID hwid, String username, Connection c) throws HWIDException { public void onCheckInfo(OshiHWID hwid, String username, Connection c) throws HWIDException {
try (PreparedStatement a = c.prepareStatement(queryHwids)) { try (PreparedStatement a = c.prepareStatement(queryHwids)) {
String[] replaceParams = {"totalMemory", String.valueOf(hwid.totalMemory), "serialNumber", hwid.serialNumber, "HWDiskSerial", hwid.HWDiskSerial, "processorID", hwid.processorID}; String[] replaceParams = {"totalMemory", String.valueOf(hwid.totalMemory), "serialNumber", hwid.serialNumber, "HWDiskSerial", hwid.HWDiskSerial, "processorID", hwid.processorID, "MAC", hwid.macAddr};
for (int i = 0; i < paramsHwids.length; i++) { for (int i = 0; i < paramsHwids.length; i++) {
a.setString(i + 1, CommonHelper.replace(paramsHwids[i], replaceParams)); a.setString(i + 1, CommonHelper.replace(paramsHwids[i], replaceParams));
} }
@ -149,7 +152,7 @@ public void onCheckInfo(OshiHWID hwid, String username, Connection c) throws HWI
db_hwid.processorID = set.getString(hwidFieldProcessorID); db_hwid.processorID = set.getString(hwidFieldProcessorID);
db_hwid.HWDiskSerial = set.getString(hwidFieldHWDiskSerial); db_hwid.HWDiskSerial = set.getString(hwidFieldHWDiskSerial);
db_hwid.totalMemory = Long.valueOf(set.getString(hwidFieldTotalMemory)); db_hwid.totalMemory = Long.valueOf(set.getString(hwidFieldTotalMemory));
db_hwid.macAddr = ""; db_hwid.macAddr = set.getString(hwidFieldMAC);
LogHelper.dev("Compare HWID: %s vs %s", hwid.getSerializeString(), db_hwid.getSerializeString()); LogHelper.dev("Compare HWID: %s vs %s", hwid.getSerializeString(), db_hwid.getSerializeString());
int compare_point = hwid.compare(db_hwid); int compare_point = hwid.compare(db_hwid);
if (compare_point < compare) continue; if (compare_point < compare) continue;
@ -177,7 +180,7 @@ public void setIsBanned(HWID hwid, boolean isBanned) {
OshiHWID oshiHWID = (OshiHWID) hwid; OshiHWID oshiHWID = (OshiHWID) hwid;
try (Connection c = mySQLHolder.getConnection()) { try (Connection c = mySQLHolder.getConnection()) {
try (PreparedStatement a = c.prepareStatement(queryBan)) { try (PreparedStatement a = c.prepareStatement(queryBan)) {
String[] replaceParamsUpd = {"totalMemory", String.valueOf(oshiHWID.totalMemory), "serialNumber", oshiHWID.serialNumber, "HWDiskSerial", oshiHWID.HWDiskSerial, "processorID", oshiHWID.processorID, "isBanned", isBanned ? "1" : "0"}; String[] replaceParamsUpd = {"totalMemory", String.valueOf(oshiHWID.totalMemory), "serialNumber", oshiHWID.serialNumber, "HWDiskSerial", oshiHWID.HWDiskSerial, "processorID", oshiHWID.processorID, "MAC", oshiHWID.macAddr, "isBanned", isBanned ? "1" : "0"};
for (int i = 0; i < paramsBan.length; i++) { for (int i = 0; i < paramsBan.length; i++) {
a.setString(i + 1, CommonHelper.replace(paramsBan[i], replaceParamsUpd)); a.setString(i + 1, CommonHelper.replace(paramsBan[i], replaceParamsUpd));
} }
@ -231,6 +234,7 @@ public List<HWID> getHwid(String username) {
oshiHWID.serialNumber = rs.getString(hwidFieldSerialNumber); oshiHWID.serialNumber = rs.getString(hwidFieldSerialNumber);
oshiHWID.HWDiskSerial = rs.getString(hwidFieldHWDiskSerial); oshiHWID.HWDiskSerial = rs.getString(hwidFieldHWDiskSerial);
oshiHWID.processorID = rs.getString(hwidFieldProcessorID); oshiHWID.processorID = rs.getString(hwidFieldProcessorID);
oshiHWID.macAddr = rs.getString(hwidFieldMAC);
list.add(oshiHWID); list.add(oshiHWID);
} }
} }

View file

@ -83,6 +83,12 @@ public void setSecretKey(String key) {
body.append("\";"); body.append("\";");
} }
public void setOemUnlockKey(String key) {
body.append("this.oemUnlockKey = \"");
body.append(key);
body.append("\";");
}
public void setGuardType(String key) { public void setGuardType(String key) {
body.append("this.guardType = \""); body.append("this.guardType = \"");
body.append(key); body.append(key);

View file

@ -42,10 +42,11 @@ public JARLauncherBinary(LaunchServer server) throws IOException {
public void init() { public void init() {
tasks.add(new PrepareBuildTask(server)); tasks.add(new PrepareBuildTask(server));
tasks.add(new MainBuildTask(server)); tasks.add(new MainBuildTask(server));
if(server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server));
tasks.add(new ProGuardBuildTask(server)); tasks.add(new ProGuardBuildTask(server));
tasks.add(new AdditionalFixesApplyTask(server)); tasks.add(new AdditionalFixesApplyTask(server));
tasks.add(new RadonBuildTask(server)); tasks.add(new RadonBuildTask(server));
tasks.add(new AttachJarsTask(server)); if(!server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server));
} }
@Override @Override

View file

@ -137,6 +137,8 @@ public Path process(Path inputJar) throws IOException {
jaConfigurator.setGuardType(server.config.launcher.guardType); jaConfigurator.setGuardType(server.config.launcher.guardType);
jaConfigurator.setWarningMissArchJava(server.config.isWarningMissArchJava); jaConfigurator.setWarningMissArchJava(server.config.isWarningMissArchJava);
jaConfigurator.setEnv(server.config.env); jaConfigurator.setEnv(server.config.env);
if(server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken();
jaConfigurator.setOemUnlockKey(server.runtime.oemUnlockKey);
server.buildHookManager.registerAllClientModuleClass(jaConfigurator); server.buildHookManager.registerAllClientModuleClass(jaConfigurator);
reader.getCp().add(new JarFile(inputJar.toFile())); reader.getCp().add(new JarFile(inputJar.toFile()));
server.launcherBinary.coreLibs.forEach(e -> { server.launcherBinary.coreLibs.forEach(e -> {

View file

@ -5,6 +5,7 @@
public class LaunchServerRuntimeConfig { public class LaunchServerRuntimeConfig {
public String clientToken; public String clientToken;
public String oemUnlockKey;
public void verify() { public void verify() {
if (clientToken == null) LogHelper.error("[RuntimeConfig] clientToken must not be null"); if (clientToken == null) LogHelper.error("[RuntimeConfig] clientToken must not be null");

View file

@ -39,7 +39,13 @@ public void execute(ChannelHandlerContext ctx, Client client) {
return; return;
} }
String url = LaunchServer.server.config.netty.downloadURL.replace("%dirname%", dirName); String url = LaunchServer.server.config.netty.downloadURL.replace("%dirname%", dirName);
if (server.config.netty.bindings.get(dirName) != null) url = server.config.netty.bindings.get(dirName); boolean zip = false;
service.sendObject(ctx, new UpdateRequestEvent(dir.object, url)); if (server.config.netty.bindings.get(dirName) != null)
{
LaunchServer.NettyUpdatesBind bind = server.config.netty.bindings.get(dirName);
url = bind.url;
zip = bind.zip;
}
service.sendObject(ctx, new UpdateRequestEvent(dir.object, url, zip));
} }
} }

View file

@ -5,7 +5,6 @@ var profilesList = [];
var movePoint = null; var movePoint = null;
var pingers = {}; var pingers = {};
var loginData; var loginData;
// Variable which contains all types of auth. Appending data at line 255
var authTypes = {}; var authTypes = {};
function initLauncher() { function initLauncher() {
@ -195,7 +194,6 @@ function goAuth(event) {
return; return;
} }
// Get auth
var auth = authOptions.getSelectionModel().getSelectedItem(); var auth = authOptions.getSelectionModel().getSelectedItem();
if (auth === null) { if (auth === null) {
return; // No auth selected return; // No auth selected
@ -220,7 +218,7 @@ function goAuth(event) {
} }
settings.login = login; settings.login = login;
doAuth(/*auth, */login, rsaPassword, authTypes[auth]); doAuth(login, rsaPassword, authTypes[auth]);
} }
/* ======== Console ======== */ /* ======== Console ======== */
@ -259,17 +257,14 @@ function verifyLauncher(e) {
result.list.forEach(function(auth_type, i, arr) { result.list.forEach(function(auth_type, i, arr) {
var serverAuth = new com.jfoenix.controls.JFXComboBox(); var serverAuth = new com.jfoenix.controls.JFXComboBox();
serverAuth.getStyleClass().add("authOptions"); serverAuth.getStyleClass().add("authOptions");
// add display name to items and add name with iter to variable authTypes
authOptions.getItems().add(auth_type.displayName); authOptions.getItems().add(auth_type.displayName);
authTypes[auth_type.displayName] = auth_type.name; authTypes[auth_type.displayName] = auth_type.name;
iter++; iter++;
}); });
authOptions.getSelectionModel().select(0); authOptions.getSelectionModel().select(0);
var sm = authOptions.getSelectionModel().selectedIndexProperty(); var sm = authOptions.getSelectionModel().selectedIndexProperty();
// add listener to authOptions select
sm.addListener(new javafx.beans.value.ChangeListener({ sm.addListener(new javafx.beans.value.ChangeListener({
changed: function (observableValue, oldSelection, newSelection) { changed: function (observableValue, oldSelection, newSelection) {
// get auth name from authTypes
settings.auth = authTypes[authOptions.getSelectionModel().getSelectedItem()]; settings.auth = authTypes[authOptions.getSelectionModel().getSelectedItem()];
} }
})); }));
@ -423,8 +418,8 @@ var overlay = {
dimPane.setVisible(true); dimPane.setVisible(true);
dimPane.toFront(); dimPane.toFront();
loginPaneLayout.setEffect(new javafx.scene.effect.GaussianBlur(55)); loginPaneLayout.setEffect(new javafx.scene.effect.GaussianBlur(10));
serverPaneLayout.setEffect(new javafx.scene.effect.GaussianBlur(55)); serverPaneLayout.setEffect(new javafx.scene.effect.GaussianBlur(10));
fade(dimPane, 0.0, 0.0, 1.0, function(event) { fade(dimPane, 0.0, 0.0, 1.0, function(event) {
dimPane.requestFocus(); dimPane.requestFocus();
dimPane.getChildren().add(newOverlay); dimPane.getChildren().add(newOverlay);

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View file

@ -17,7 +17,6 @@ var processing = {
setError: function(e) { setError: function(e) {
LogHelper.error(e); LogHelper.error(e);
processing.description.textProperty().unbind(); processing.description.textProperty().unbind();
//processing.errorImage.setImage(processing.errorImage);
processing.description.getStyleClass().add("error"); processing.description.getStyleClass().add("error");
processing.description.setText(e.toString()); processing.description.setText(e.toString());
}, },
@ -48,8 +47,6 @@ function offlineAuthRequest(login) {
Request.requestError("Имя пользователя некорректно"); Request.requestError("Имя пользователя некорректно");
return; return;
} }
// Return offline profile and random access token
return { return {
pp: PlayerProfile.newOfflineProfile(login), pp: PlayerProfile.newOfflineProfile(login),
accessToken: SecurityHelper.randomStringToken() accessToken: SecurityHelper.randomStringToken()
@ -57,18 +54,15 @@ function offlineAuthRequest(login) {
}; };
} }
/* Export functions */
function makeLauncherRequest(callback) { function makeLauncherRequest(callback) {
var task = settings.offline ? newTask(FunctionalBridge.offlineLauncherRequest) : var task = settings.offline ? newTask(FunctionalBridge.offlineLauncherRequest) :
newRequestTask(new LauncherRequest()); newRequestTask(new LauncherRequest());
// Set task properties and start
processing.setTaskProperties(task, callback, function() { processing.setTaskProperties(task, callback, function() {
if (settings.offline) { if (settings.offline) {
return; return;
} }
// Repeat request, but in offline mode
settings.offline = true; settings.offline = true;
overlay.swap(2500, processing.overlay, function() makeLauncherRequest(callback)); overlay.swap(2500, processing.overlay, function() makeLauncherRequest(callback));
}, false); }, false);
@ -78,13 +72,11 @@ function makeLauncherRequest(callback) {
function makeProfilesRequest(callback) { function makeProfilesRequest(callback) {
var task = newRequestTask(new ProfilesRequest()); var task = newRequestTask(new ProfilesRequest());
// Set task properties and start
processing.setTaskProperties(task, callback, function() { processing.setTaskProperties(task, callback, function() {
if (settings.offline) { if (settings.offline) {
return; return;
} }
// Repeat request, but in offline mode
settings.offline = true; settings.offline = true;
overlay.swap(2500, processing.overlay, function() makeProfilesRequest(callback)); overlay.swap(2500, processing.overlay, function() makeProfilesRequest(callback));
}, false); }, false);
@ -94,7 +86,6 @@ function makeProfilesRequest(callback) {
function makeAuthAvailabilityRequest(callback) { function makeAuthAvailabilityRequest(callback) {
var task = newRequestTask(new GetAvailabilityAuthRequest()); var task = newRequestTask(new GetAvailabilityAuthRequest());
// Set task properties and start
processing.setTaskProperties(task, callback, function() { processing.setTaskProperties(task, callback, function() {
if (settings.offline) { if (settings.offline) {
return; return;
@ -110,7 +101,6 @@ function makeAuthAvailabilityRequest(callback) {
function makeSetProfileRequest(profile, callback) { function makeSetProfileRequest(profile, callback) {
var task = newRequestTask(new SetProfileRequest(profile)); var task = newRequestTask(new SetProfileRequest(profile));
// Set task properties and start
processing.setTaskProperties(task, callback, function() { processing.setTaskProperties(task, callback, function() {
if (settings.offline) { if (settings.offline) {
return; return;

View file

@ -3,7 +3,7 @@
#overlay { #overlay {
-fx-background-color: transparent; -fx-background-color: transparent;
-fx-background-size: cover; -fx-background-size: cover;
-fx-background-image: url('../../images/background.jpg'); -fx-background-image: url('../../images/downloader/blured.jpg');
} }
#overlay > #utitle { #overlay > #utitle {
@ -20,7 +20,7 @@ #overlay > #description.error {
} }
.downloadPane { .downloadPane {
-fx-background-color: rgba(0, 0, 0, 0.3); -fx-background-color: rgba(0, 0, 0, 0.2);
} }
/* Progress bar */ /* Progress bar */

View file

@ -8,29 +8,27 @@
<!-- DrLeonardo Design --> <!-- DrLeonardo Design -->
<Pane fx:id="overlay" prefHeight="450.0" prefWidth="693.0" xmlns="http://javafx.com/javafx/8.0.201" <Pane fx:id="overlay" prefHeight="450.0" prefWidth="694.0" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1">
xmlns:fx="http://javafx.com/fxml/1"> <children>
<children> <Pane prefHeight="450.0" prefWidth="694.0" styleClass="downloadPane">
<Pane prefHeight="450.0" prefWidth="693.0" styleClass="downloadPane"> <children>
<children> <Label fx:id="utitle" alignment="CENTER" layoutX="100.0" layoutY="125.0" prefHeight="30.0" prefWidth="495.0" text="Загрузка обновления..." textFill="WHITE">
<Label fx:id="utitle" alignment="CENTER" layoutX="100.0" layoutY="125.0" prefHeight="30.0" <font>
prefWidth="495.0" text="Загрузка обновления..." textFill="WHITE"> <Font name="System Bold" size="20.0" />
<font> </font>
<Font name="System Bold" size="20.0"/> </Label>
</font> <JFXSpinner fx:id="progress" layoutX="98.0" layoutY="226.0" prefHeight="100.0" prefWidth="100.0" />
</Label> <Label fx:id="description" layoutX="216.0" layoutY="226.0" prefHeight="100.0" prefWidth="380.0" text="..." textFill="WHITE">
<JFXSpinner fx:id="progress" layoutX="98.0" layoutY="226.0" prefHeight="100.0" prefWidth="100.0"/> <font>
<Label fx:id="description" layoutX="216.0" layoutY="226.0" prefHeight="100.0" prefWidth="380.0" <Font name="System Bold" size="16.0" />
text="..." textFill="WHITE"> </font>
<font> </Label>
<Font name="System Bold" size="16.0"/> </children>
</font> </Pane>
</Label> <Pane id="mask" layoutX="-1.0" opacity="0.0" prefHeight="450.0" prefWidth="694.0" visible="false" />
</children> </children>
</Pane> <stylesheets>
</children> <URL value="@update.css" />
<stylesheets> <URL value="@../../styles.css" />
<URL value="@update.css"/> </stylesheets>
<URL value="@../../styles.css"/>
</stylesheets>
</Pane> </Pane>

View file

@ -4,6 +4,9 @@ var update = {
initOverlay: function() { initOverlay: function() {
update.overlay = loadFXML("dialog/overlay/update/update.fxml"); update.overlay = loadFXML("dialog/overlay/update/update.fxml");
//var updateLayout = update.overlay.lookup("#overlay");
//serverPaneLayout = updateLayout;
update.title = update.overlay.lookup("#utitle"); update.title = update.overlay.lookup("#utitle");
update.description = update.overlay.lookup("#description"); update.description = update.overlay.lookup("#description");
update.progress = update.overlay.lookup("#progress"); update.progress = update.overlay.lookup("#progress");

View file

@ -15,62 +15,51 @@
<!-- DrLeonardo Design --> <!-- DrLeonardo Design -->
<Pane fx:id="loginPane" prefWidth="740.0" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1"> <Pane fx:id="loginPane" prefWidth="740.0" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<Pane fx:id="layout" prefWidth="740.0"> <Pane fx:id="layout" prefWidth="740.0">
<children> <children>
<Pane fx:id="authPane" layoutX="422.0" prefHeight="411.0" prefWidth="286.0" styleClass="loginPane"> <Pane fx:id="authPane" layoutX="424.0" prefHeight="411.0" prefWidth="286.0" styleClass="loginPane">
<children> <children>
<Pane fx:id="logo" layoutX="72.0" layoutY="62.0" prefWidth="124.0" styleClass="logo"> <Pane fx:id="logo" layoutX="72.0" layoutY="62.0" prefWidth="124.0" styleClass="logo">
</Pane> </Pane>
<JFXTextField id="login" alignment="CENTER" focusColor="#5fd97a" layoutX="34.0" layoutY="196.0" <JFXTextField id="login" alignment="CENTER" focusColor="#5fd97a" layoutX="34.0" layoutY="196.0" promptText="Логин" unFocusColor="#dadada" />
promptText="Логин" unFocusColor="#dadada"/> <JFXPasswordField id="password" alignment="CENTER" focusColor="#5fd97a" layoutX="34.0" layoutY="249.0" promptText="Пароль" unFocusColor="#dadada" />
<JFXPasswordField id="password" alignment="CENTER" focusColor="#5fd97a" layoutX="34.0" <JFXButton id="goAuth" layoutX="34.0" layoutY="370.0" styleClass="auth" text="ВОЙТИ" />
layoutY="249.0" promptText="Пароль" unFocusColor="#dadada"/> <JFXCheckBox id="rememberchb" fx:id="savePassword" checkedColor="#5fd97a" contentDisplay="CENTER" layoutX="63.0" layoutY="297.0" prefWidth="144.0" text="Сохранить пароль" textFill="#dadada" unCheckedColor="#909090" />
<JFXButton id="goAuth" layoutX="34.0" layoutY="370.0" styleClass="auth" text="ВОЙТИ"/> <JFXComboBox id="authOptions" fx:id="authOptions" focusColor="#5fd97a" layoutX="34.0" layoutY="341.0" prefHeight="25.0" prefWidth="200.0" promptText="Способ авторизации" unFocusColor="#70666600">
<JFXCheckBox id="rememberchb" fx:id="savePassword" checkedColor="#5fd97a" <styleClass>
contentDisplay="CENTER" layoutX="63.0" layoutY="297.0" prefWidth="144.0" <String fx:value="combologin" />
text="Сохранить пароль" textFill="#dadada" unCheckedColor="#909090"/> <String fx:value="combologin-popup" />
<JFXComboBox id="authOptions" fx:id="authOptions" focusColor="#5fd97a" layoutX="34.0" </styleClass>
layoutY="341.0" prefHeight="25.0" prefWidth="200.0" promptText="Авторизация 1" </JFXComboBox>
unFocusColor="#70666600"> <Hyperlink id="link" fx:id="link" layoutX="94.0" layoutY="422.0" prefHeight="19.0" prefWidth="81.0" textAlignment="CENTER" />
<styleClass> </children>
<String fx:value="combologin"/> </Pane>
<String fx:value="combologin-popup"/> <JFXMasonryPane fx:id="news" prefHeight="432.0" prefWidth="423.0" styleClass="news" />
</styleClass> </children>
</JFXComboBox> </Pane>
<Hyperlink id="link" fx:id="link" layoutX="94.0" layoutY="422.0" prefHeight="19.0" <Pane id="mask" opacity="0.0" prefHeight="450.0" prefWidth="694.0" visible="false" />
prefWidth="81.0" textAlignment="CENTER"/> <Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar">
</children> <children>
</Pane> <JFXButton id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" ripplerFill="#646464" text="" textAlignment="CENTER">
<JFXMasonryPane fx:id="news" prefHeight="432.0" prefWidth="423.0" styleClass="news"/> <graphic>
</children> <MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER" />
</Pane> </graphic>
<Pane id="mask" opacity="0.0" prefHeight="425.0" prefWidth="694.0" visible="false"/> </JFXButton>
<Pane fx:id="bar" layoutX="696.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar"> <JFXButton id="close" alignment="CENTER" contentDisplay="CENTER" ripplerFill="#fb8c8c" text="" textAlignment="CENTER">
<children> <graphic>
<JFXButton id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" ripplerFill="#646464" <MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER" />
text="" textAlignment="CENTER"> </graphic>
<graphic> </JFXButton>
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER"/> <JFXButton id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="370.0" ripplerFill="#646464" text="" textAlignment="CENTER">
</graphic> <graphic>
</JFXButton> <MaterialDesignIconView fill="#5fd97a" glyphName="MESSAGE_TEXT" size="20" textAlignment="CENTER" />
<JFXButton id="close" alignment="CENTER" contentDisplay="CENTER" ripplerFill="#fb8c8c" text="" </graphic>
textAlignment="CENTER"> </JFXButton>
<graphic> </children>
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER"/> </Pane>
</graphic> </children>
</JFXButton> <stylesheets>
<JFXButton id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="370.0" ripplerFill="#646464" <URL value="@../../styles.css" />
text="" textAlignment="CENTER"> </stylesheets>
<graphic>
<MaterialDesignIconView fill="#5fd97a" glyphName="MESSAGE_TEXT" size="20"
textAlignment="CENTER"/>
</graphic>
</JFXButton>
</children>
</Pane>
</children>
<stylesheets>
<URL value="@../../styles.css"/>
</stylesheets>
</Pane> </Pane>

View file

@ -13,127 +13,98 @@
<!-- DrLeonardo Design --> <!-- DrLeonardo Design -->
<Pane fx:id="serverPaneLayout" maxHeight="-1.0" maxWidth="-1.0" prefWidth="740.0" visible="true" <Pane fx:id="serverPaneLayout" maxHeight="-1.0" maxWidth="-1.0" prefWidth="740.0" visible="true" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1">
xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<Pane fx:id="layout" maxHeight="-1.0" maxWidth="-1.0" prefWidth="740.0" visible="true" <Pane fx:id="layout" maxHeight="-1.0" maxWidth="-1.0" prefWidth="740.0" visible="true" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1">
xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<Pane id="serverPane" prefHeight="450.0" prefWidth="693.0"> <Pane id="serverPane" prefHeight="450.0" prefWidth="694.0">
<children> <children>
<ScrollPane id="serverlist" hbarPolicy="NEVER" layoutX="1.0" prefHeight="450.0" <ScrollPane id="serverlist" hbarPolicy="NEVER" layoutX="1.0" prefHeight="450.0" prefWidth="307.0" visible="true">
prefWidth="307.0" visible="true">
<content> <content>
<FlowPane focusTraversable="false" prefHeight="446.0" prefWidth="306.0" <FlowPane focusTraversable="false" prefHeight="446.0" prefWidth="306.0" prefWrapLength="0.0" rowValignment="TOP" vgap="10.0" visible="true">
prefWrapLength="0.0" rowValignment="TOP" vgap="10.0" visible="true">
<JFXButton id="servercontainer" ripplerFill="#ffffff80" text=""> <JFXButton id="servercontainer" ripplerFill="#ffffff80" text="">
<FlowPane.margin> <FlowPane.margin>
<Insets bottom="10.0"/> <Insets bottom="10.0" />
</FlowPane.margin> </FlowPane.margin></JFXButton>
</JFXButton>
<padding> <padding>
<Insets left="10.0" top="10.0"/> <Insets left="10.0" top="10.0" />
</padding> </padding>
</FlowPane> </FlowPane>
</content> </content>
</ScrollPane> </ScrollPane>
<Pane id="serverentrance" layoutX="306.0" prefHeight="425.0" prefWidth="388.0" <Pane id="serverentrance" layoutX="308.0" prefHeight="425.0" prefWidth="388.0" styleClass="serverentrance">
styleClass="serverentrance">
<children> <children>
<ScrollPane id="serverinfo" hbarPolicy="NEVER" layoutX="4.0" layoutY="53.0" <ScrollPane id="serverinfo" hbarPolicy="NEVER" layoutX="4.0" layoutY="53.0" pannable="true" prefHeight="322.0" prefWidth="381.0" visible="true">
pannable="true" prefHeight="322.0" prefWidth="381.0" visible="true">
<content> <content>
<FlowPane id="" focusTraversable="false" orientation="HORIZONTAL" <FlowPane id="" focusTraversable="false" orientation="HORIZONTAL" prefHeight="310.0" prefWidth="369.0" rowValignment="TOP" visible="true">
prefHeight="310.0" prefWidth="369.0" rowValignment="TOP"
visible="true">
<padding> <padding>
<Insets bottom="10.0" left="15.0" top="7.0"/> <Insets bottom="10.0" left="15.0" top="7.0" />
</padding> </padding>
<children> <children>
<Label id="serverDescription" alignment="TOP_LEFT" contentDisplay="LEFT" <Label id="serverDescription" alignment="TOP_LEFT" contentDisplay="LEFT" nodeOrientation="LEFT_TO_RIGHT" prefHeight="274.0" prefWidth="349.0" text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla convallis magna tellus, in bibendum tortor dignissim non. Phasellus vel tincidunt nulla, eu convallis ligula. Suspendisse ut diam vestibulum, tincidunt neque ut, posuere risus. Pellentesque posuere molestie eros, quis laoreet ante ornare quis. Morbi eu tortor fermentum, iaculis risus sit amet, fringilla augue. Aenean nulla purus, rutrum non sapien et, convallis tincidunt purus. Vivamus a eros pulvinar, dignissim leo lacinia, sodales nulla. Aliquam tortor augue, cursus a rutrum viverra, consequat non tellus. Donec porta nisl sed quam dictum commodo. Sed et vulputate dolor. Morbi ultrices justo vitae convallis semper. Donec sodales velit vel velit faucibus, et scelerisque felis finibus. Sed rutrum lacinia mauris, porta cursus mauris tempor eu. Duis turpis nulla, dictum vitae commodo rhoncus, pretium in turpis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos." textAlignment="JUSTIFY" textFill="#141414" wrapText="true" />
nodeOrientation="LEFT_TO_RIGHT" prefHeight="274.0"
prefWidth="349.0"
text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla convallis magna tellus, in bibendum tortor dignissim non. Phasellus vel tincidunt nulla, eu convallis ligula. Suspendisse ut diam vestibulum, tincidunt neque ut, posuere risus. Pellentesque posuere molestie eros, quis laoreet ante ornare quis. Morbi eu tortor fermentum, iaculis risus sit amet, fringilla augue. Aenean nulla purus, rutrum non sapien et, convallis tincidunt purus. Vivamus a eros pulvinar, dignissim leo lacinia, sodales nulla. Aliquam tortor augue, cursus a rutrum viverra, consequat non tellus. Donec porta nisl sed quam dictum commodo. Sed et vulputate dolor. Morbi ultrices justo vitae convallis semper. Donec sodales velit vel velit faucibus, et scelerisque felis finibus. Sed rutrum lacinia mauris, porta cursus mauris tempor eu. Duis turpis nulla, dictum vitae commodo rhoncus, pretium in turpis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."
textAlignment="JUSTIFY" textFill="#141414" wrapText="true"/>
</children> </children>
</FlowPane> </FlowPane>
</content> </content>
</ScrollPane> </ScrollPane>
<JFXButton id="clientLaunch" layoutX="19.0" layoutY="380.0" prefHeight="51.0" <JFXButton id="clientLaunch" layoutX="19.0" layoutY="380.0" prefHeight="51.0" prefWidth="285.0" styleClass="clientLaunch" text="ИГРАТЬ">
prefWidth="285.0" styleClass="clientLaunch" text="ИГРАТЬ">
<font> <font>
<Font size="22.0"/> <Font size="22.0" />
</font> </font>
</JFXButton> </JFXButton>
<JFXButton id="clientSettings" alignment="CENTER" centerShape="false" <JFXButton id="clientSettings" alignment="CENTER" centerShape="false" contentDisplay="CENTER" layoutX="305.0" layoutY="380.0" prefHeight="51.0" prefWidth="60.0" ripplerFill="#84da96" styleClass="clientSettings" text="" textAlignment="CENTER">
contentDisplay="CENTER" layoutX="305.0" layoutY="380.0" prefHeight="51.0"
prefWidth="60.0" ripplerFill="#84da96" styleClass="clientSettings" text=""
textAlignment="CENTER">
<graphic> <graphic>
<FontAwesomeIconView fill="WHITE" glyphName="SLIDERS" size="30.0"/> <FontAwesomeIconView fill="WHITE" glyphName="SLIDERS" size="30.0" />
</graphic> </graphic></JFXButton>
</JFXButton> <Label id="serverStatus" alignment="TOP_RIGHT" contentDisplay="RIGHT" layoutX="165.0" layoutY="12.0" prefHeight="25.0" prefWidth="97.0" text="12/100" textAlignment="RIGHT" textFill="WHITE">
<Label id="serverStatus" alignment="TOP_RIGHT" contentDisplay="RIGHT" layoutX="165.0"
layoutY="12.0" prefHeight="25.0" prefWidth="97.0" text="12/100"
textAlignment="RIGHT" textFill="WHITE">
<font> <font>
<Font name="System Bold" size="16.0"/> <Font name="System Bold" size="16.0" />
</font> </font>
</Label> </Label>
<Label id="serverLabel" layoutX="20.0" layoutY="11.0" prefHeight="27.0" <Label id="serverLabel" layoutX="20.0" layoutY="11.0" prefHeight="27.0" prefWidth="203.0" text="СЕРВЕР">
prefWidth="203.0" text="СЕРВЕР">
<font> <font>
<Font name="System Bold" size="18.0"/> <Font name="System Bold" size="18.0" />
</font> </font>
</Label> </Label>
<JFXButton id="logout" alignment="CENTER" contentDisplay="CENTER" layoutX="295.0" <JFXButton id="logout" alignment="CENTER" contentDisplay="CENTER" layoutX="295.0" layoutY="12.0" prefHeight="25.0" prefWidth="81.0" ripplerFill="#61616100" text="Выйти" textAlignment="CENTER" />
layoutY="12.0" prefHeight="25.0" prefWidth="81.0" ripplerFill="#61616100"
text="Выйти" textAlignment="CENTER"/>
</children> </children>
</Pane> </Pane>
</children> </children>
</Pane> </Pane>
</children> </children>
</Pane> </Pane>
<Pane fx:id="bar" layoutX="696.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar"> <Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar">
<children> <children>
<JFXButton id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" ripplerFill="#646464" <JFXButton id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" ripplerFill="#646464" text="" textAlignment="CENTER">
text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER"/> <MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER" />
</graphic> </graphic>
</JFXButton> </JFXButton>
<JFXButton id="close" alignment="CENTER" contentDisplay="CENTER" ripplerFill="#fb8c8c" text="" <JFXButton id="close" alignment="CENTER" contentDisplay="CENTER" ripplerFill="#fb8c8c" text="" textAlignment="CENTER">
textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER"/> <MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER" />
</graphic> </graphic>
</JFXButton> </JFXButton>
<JFXButton id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="380.0" ripplerFill="#646464" <JFXButton id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="380.0" ripplerFill="#646464" text="" textAlignment="CENTER">
text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="#5fd97a" glyphName="MESSAGE_TEXT" size="20" smooth="false" <MaterialDesignIconView fill="#5fd97a" glyphName="MESSAGE_TEXT" size="20" smooth="false" textAlignment="CENTER" />
textAlignment="CENTER"/>
</graphic> </graphic>
</JFXButton> </JFXButton>
<JFXButton id="settings" alignment="CENTER" contentDisplay="CENTER" layoutY="90.0" ripplerFill="#646464" <JFXButton id="settings" alignment="CENTER" contentDisplay="CENTER" layoutY="90.0" ripplerFill="#646464" text="" textAlignment="CENTER">
text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="SETTINGS" size="20" textAlignment="CENTER"/> <MaterialDesignIconView fill="WHITE" glyphName="SETTINGS" size="20" textAlignment="CENTER" />
</graphic> </graphic>
</JFXButton> </JFXButton>
<JFXButton id="goConsole" alignment="CENTER" contentDisplay="CENTER" layoutY="138.0" <JFXButton id="goConsole" alignment="CENTER" contentDisplay="CENTER" layoutY="138.0" ripplerFill="#646464" text="" textAlignment="CENTER">
ripplerFill="#646464" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="CONSOLE" size="20" textAlignment="CENTER"/> <MaterialDesignIconView fill="WHITE" glyphName="CONSOLE" size="20" textAlignment="CENTER" />
</graphic> </graphic>
</JFXButton> </JFXButton>
</children> </children>
</Pane> </Pane>
<Pane id="mask" opacity="0.0" prefHeight="425.0" prefWidth="694.0" visible="false"/> <Pane id="mask" opacity="0.0" prefHeight="450.0" prefWidth="694.0" visible="false" />
</children> </children>
<stylesheets> <stylesheets>
<URL value="@../../styles.css"/> <URL value="@../../styles.css" />
<URL value="@../../servers.css"/> <URL value="@../../servers.css" />
</stylesheets> </stylesheets>
</Pane> </Pane>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?> <?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXToggleButton?>
<?import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView?> <?import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView?>
<?import java.net.URL?> <?import java.net.URL?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
@ -9,59 +8,55 @@
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Line?> <?import javafx.scene.shape.Line?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<!-- DrLeonardo Design --> <!-- DrLeonardo Design -->
<Pane fx:id="background" prefHeight="450.0" prefWidth="738.0" xmlns="http://javafx.com/javafx/8.0.201" <Pane fx:id="background" prefHeight="450.0" prefWidth="740.0" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1">
xmlns:fx="http://javafx.com/fxml/1"> <children>
<children> <Pane id="optionsPane" prefHeight="450.0" prefWidth="692.0" styleClass="optionsPane">
<Pane id="optionsPane" prefHeight="450.0" prefWidth="692.0" styleClass="optionsPane"> <children>
<children> <Line endX="595.0" layoutX="100.0" layoutY="46.0" startX="-100.0" stroke="#5b3636" styleClass="lineHead" />
<JFXToggleButton fx:id="presset" layoutX="30.0" layoutY="10.0" opacity="0.21" styleClass="pressetLight" <ScrollPane id="modlist" layoutY="46.0" prefHeight="402.0" prefWidth="693.0">
text="Presset 1"/> <content>
<JFXToggleButton fx:id="presset" layoutX="287.0" layoutY="10.0" opacity="0.21" <VBox prefHeight="397.0" prefWidth="678.0">
styleClass="pressetMedium" text="Presset 2"/> <children>
<JFXToggleButton fx:id="isPresset" layoutX="528.0" layoutY="10.0" opacity="0.21" prefHeight="58.0" </children>
prefWidth="134.0" styleClass="pressetHigh" text="Presset 3" wrapText="true"/> <padding>
<Line endX="595.0" layoutX="100.0" layoutY="80.0" startX="-100.0" stroke="#5b3636" <Insets left="10.0" top="8.0" />
styleClass="lineHead"/> </padding>
<ScrollPane id="modlist" layoutY="84.0" prefHeight="364.0" prefWidth="693.0"> </VBox>
<content> </content>
<VBox prefHeight="360.0" prefWidth="678.0"> </ScrollPane>
<children> <Text fill="#393939" layoutX="15.0" layoutY="28.0" strokeType="OUTSIDE" strokeWidth="0.0" text="ОПЦИОНАЛЬНЫЕ МОДИФИКАЦИИ" wrappingWidth="265.904296875">
</children> <font>
<padding> <Font name="System Bold" size="13.0" />
<Insets left="10.0" top="8.0"/> </font>
</padding> </Text>
</VBox> </children>
</content> </Pane>
</ScrollPane> <Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar">
</children> <children>
</Pane> <JFXButton id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" ripplerFill="#646464" text="" textAlignment="CENTER">
<Pane fx:id="bar" layoutX="692.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar"> <graphic>
<children> <MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER" />
<JFXButton id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" ripplerFill="#646464" </graphic>
text="" textAlignment="CENTER"> </JFXButton>
<graphic> <JFXButton id="close" alignment="CENTER" contentDisplay="CENTER" ripplerFill="#fb8c8c" text="" textAlignment="CENTER">
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER"/> <graphic>
</graphic> <MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER" />
</JFXButton> </graphic>
<JFXButton id="close" alignment="CENTER" contentDisplay="CENTER" ripplerFill="#fb8c8c" text="" </JFXButton>
textAlignment="CENTER"> <JFXButton id="back" alignment="CENTER" contentDisplay="CENTER" layoutY="405.0" ripplerFill="#646464" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER"/> <MaterialDesignIconView fill="WHITE" glyphName="CHEVRON_LEFT" size="30" textAlignment="CENTER" />
</graphic> </graphic>
</JFXButton> </JFXButton>
<JFXButton id="back" alignment="CENTER" contentDisplay="CENTER" layoutY="405.0" ripplerFill="#646464" </children>
text="" textAlignment="CENTER"> </Pane>
<graphic> </children>
<MaterialDesignIconView fill="WHITE" glyphName="CHEVRON_LEFT" size="30" textAlignment="CENTER"/> <stylesheets>
</graphic> <URL value="@../../styles.css" />
</JFXButton> </stylesheets>
</children>
</Pane>
</children>
<stylesheets>
<URL value="@../../styles.css"/>
</stylesheets>
</Pane> </Pane>

View file

@ -34,8 +34,6 @@ #serverStatus{
/* Mask */ /* Mask */
#mask { #mask {
-fx-effect: DropShadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 ); -fx-effect: DropShadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 );
-fx-pref-width: 692px;
-fx-pref-height: 450px;
} }
/** Errors **/ /** Errors **/
@ -194,7 +192,7 @@ .combologin-popup .list-view {
.combologin .list-cell:filled:selected .text, .combologin .list-cell:filled:selected .text,
.combologin .list-cell:filled:selected .text { .combologin .list-cell:filled:selected .text {
-fx-fill: #909090; -fx-fill: #323232;
} }
.combologin .arrow, .combologin .arrow,
@ -211,13 +209,13 @@ .combologin-popup .list-view .list-cell:filled:selected, .combologin-popup .list
{ {
-fx-background: -fx-accent; -fx-background: -fx-accent;
-fx-background-color: -fx-selection-bar; -fx-background-color: -fx-selection-bar;
-fx-text-fill: -fx-selection-bar-text; -fx-text-fill: #909090;
} }
.combologin-popup .list-view .list-cell:filled:hover .combologin-popup .list-view .list-cell:filled:hover
{ {
-fx-background-color: white; -fx-background-color: white;
-fx-text-fill: -fx-text-inner-color; -fx-text-fill: #909090;
} }
/** web**/ /** web**/

View file

@ -3,6 +3,7 @@
import ru.gravit.launcher.client.ClientModuleManager; import ru.gravit.launcher.client.ClientModuleManager;
import ru.gravit.launcher.client.DirBridge; import ru.gravit.launcher.client.DirBridge;
import ru.gravit.launcher.client.FunctionalBridge; import ru.gravit.launcher.client.FunctionalBridge;
import ru.gravit.launcher.client.LauncherUpdateController;
import ru.gravit.launcher.guard.LauncherGuardManager; import ru.gravit.launcher.guard.LauncherGuardManager;
import ru.gravit.launcher.gui.JSRuntimeProvider; import ru.gravit.launcher.gui.JSRuntimeProvider;
import ru.gravit.launcher.gui.RuntimeProvider; import ru.gravit.launcher.gui.RuntimeProvider;
@ -11,6 +12,7 @@
import ru.gravit.launcher.request.Request; import ru.gravit.launcher.request.Request;
import ru.gravit.launcher.request.RequestException; import ru.gravit.launcher.request.RequestException;
import ru.gravit.launcher.request.auth.RestoreSessionRequest; import ru.gravit.launcher.request.auth.RestoreSessionRequest;
import ru.gravit.launcher.request.update.UpdateRequest;
import ru.gravit.launcher.request.websockets.StandartClientWebSocketService; import ru.gravit.launcher.request.websockets.StandartClientWebSocketService;
import ru.gravit.utils.helper.CommonHelper; import ru.gravit.utils.helper.CommonHelper;
import ru.gravit.utils.helper.EnvHelper; import ru.gravit.utils.helper.EnvHelper;
@ -95,6 +97,7 @@ public void start(String... args) throws Throwable {
}; };
} }
LauncherGuardManager.initGuard(false); LauncherGuardManager.initGuard(false);
UpdateRequest.setController(new LauncherUpdateController());
Objects.requireNonNull(args, "args"); Objects.requireNonNull(args, "args");
if (started.getAndSet(true)) if (started.getAndSet(true))
throw new IllegalStateException("Launcher has been already started"); throw new IllegalStateException("Launcher has been already started");

View file

@ -37,6 +37,10 @@ public class NewLauncherSettings {
public List<ClientProfile> lastProfiles = new LinkedList<>(); public List<ClientProfile> lastProfiles = new LinkedList<>();
@LauncherAPI @LauncherAPI
public Map<String, UserSettings> userSettings = new HashMap<>(); public Map<String, UserSettings> userSettings = new HashMap<>();
@LauncherAPI
public boolean featureStore;
@LauncherAPI
public String consoleUnlockKey;
public static class HashedStoreEntry { public static class HashedStoreEntry {
@LauncherAPI @LauncherAPI
@ -45,6 +49,8 @@ public static class HashedStoreEntry {
public String name; public String name;
@LauncherAPI @LauncherAPI
public String fullPath; public String fullPath;
@LauncherAPI
public transient boolean needSave = false;
public HashedStoreEntry(HashedDir hdir, String name, String fullPath) { public HashedStoreEntry(HashedDir hdir, String name, String fullPath) {
this.hdir = hdir; this.hdir = hdir;
@ -59,9 +65,9 @@ public HashedStoreEntry(HashedDir hdir, String name, String fullPath) {
@LauncherAPI @LauncherAPI
public void putHDir(String name, Path path, HashedDir dir) { public void putHDir(String name, Path path, HashedDir dir) {
String fullPath = path.toAbsolutePath().toString(); String fullPath = path.toAbsolutePath().toString();
for (HashedStoreEntry e : lastHDirs) { lastHDirs.removeIf((e) -> e.fullPath.equals(fullPath) && e.name.equals(name));
if (e.fullPath.equals(fullPath) && e.name.equals(name)) return; HashedStoreEntry e = new HashedStoreEntry(dir, name, fullPath);
} e.needSave = true;
lastHDirs.add(new HashedStoreEntry(dir, name, fullPath)); lastHDirs.add(e);
} }
} }

View file

@ -0,0 +1,139 @@
package ru.gravit.launcher.client;
import ru.gravit.launcher.NewLauncherSettings;
import ru.gravit.launcher.downloader.ListDownloader;
import ru.gravit.launcher.events.request.UpdateRequestEvent;
import ru.gravit.launcher.hasher.HashedDir;
import ru.gravit.launcher.hasher.HashedEntry;
import ru.gravit.launcher.hasher.HashedFile;
import ru.gravit.launcher.managers.SettingsManager;
import ru.gravit.launcher.request.update.UpdateRequest;
import ru.gravit.utils.helper.IOHelper;
import ru.gravit.utils.helper.LogHelper;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
public class LauncherUpdateController implements UpdateRequest.UpdateController {
@Override
public void preUpdate(UpdateRequest request, UpdateRequestEvent e) {
}
@Override
public void preDiff(UpdateRequest request, UpdateRequestEvent e) {
}
@Override
public void postDiff(UpdateRequest request, UpdateRequestEvent e, HashedDir.Diff diff) throws IOException {
if(e.zip) return;
if(SettingsManager.settings.featureStore)
{
LogHelper.info("Enabled HStore feature. Find");
AtomicReference<NewLauncherSettings.HashedStoreEntry> lastEn = new AtomicReference<>(null);
ArrayList<String> removed = new ArrayList<>();
diff.mismatch.walk(File.separator, (path, name, entry) -> {
if(entry.getType() == HashedEntry.Type.DIR) {
Files.createDirectories(request.getDir().resolve(path));
return HashedDir.WalkAction.CONTINUE;
}
HashedFile file = (HashedFile) entry;
//Первый экспериментальный способ - честно обходим все возможные Store
Path ret = null;
if(lastEn.get() == null)
{
for(NewLauncherSettings.HashedStoreEntry en : SettingsManager.settings.lastHDirs)
{
ret = tryFind(en, file);
if(ret != null) {
lastEn.set(en);
break;
}
}
}
else {
ret = tryFind(lastEn.get(), file);
}
if(ret == null)
{
for(NewLauncherSettings.HashedStoreEntry en : SettingsManager.settings.lastHDirs)
{
ret = tryFind(en, file);
if(ret != null) {
lastEn.set(en);
break;
}
}
}
if(ret != null)
{
//Еще раз проверим корректность хеша
//Возможно эта проверка избыточна
//if(file.isSame(ret, true))
{
Path source = request.getDir().resolve(path);
LogHelper.debug("Copy file %s to %s", ret.toAbsolutePath().toString(), source.toAbsolutePath().toString());
//Let's go!
Files.copy(ret, source);
try(InputStream input = IOHelper.newInput(ret))
{
IOHelper.transfer(input, source);
}
entry.flag = true;
//removed.add(path.replace('\\', '/'));
}
}
return HashedDir.WalkAction.CONTINUE;
});
}
}
public Path tryFind(NewLauncherSettings.HashedStoreEntry en, HashedFile file) throws IOException
{
AtomicReference<Path> ret = new AtomicReference<>(null);
en.hdir.walk(File.separator, (path, name, entry) -> {
if(entry.getType() == HashedEntry.Type.DIR) return HashedDir.WalkAction.CONTINUE;
HashedFile tfile = (HashedFile) entry;
if(tfile.isSame(file))
{
LogHelper.dev("[DIR:%s] Found file %s in %s", en.name, name, path);
Path tdir = Paths.get(en.fullPath).resolve(path);
try {
if(tfile.isSame(tdir, true))
{
LogHelper.dev("[DIR:%s] Confirmed file %s in %s", en.name, name, path);
ret.set(tdir);
return HashedDir.WalkAction.STOP;
}
} catch (IOException e)
{
LogHelper.error("Check file error %s %s", e.getClass().getName(), e.getMessage());
}
}
return HashedDir.WalkAction.CONTINUE;
});
return ret.get();
}
@Override
public void preDownload(UpdateRequest request, UpdateRequestEvent e, List<ListDownloader.DownloadTask> adds) {
}
@Override
public void postDownload(UpdateRequest request, UpdateRequestEvent e) {
}
@Override
public void postUpdate(UpdateRequest request, UpdateRequestEvent e) {
}
}

View file

@ -0,0 +1,37 @@
package ru.gravit.launcher.console;
import ru.gravit.launcher.managers.SettingsManager;
import ru.gravit.utils.command.Command;
import ru.gravit.utils.helper.LogHelper;
public class FeatureCommand extends Command {
@Override
public String getArgsDescription() {
return "[feature] [true/false]";
}
@Override
public String getUsageDescription() {
return "Enable or disable feature";
}
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 2);
boolean enabled = Boolean.valueOf(args[1]);
switch (args[0])
{
case "store":
{
SettingsManager.settings.featureStore = enabled;
break;
}
default:
{
LogHelper.info("Features: [store]");
return;
}
}
LogHelper.info("Feature %s %s", args[0], enabled ? "enabled" : "disabled");
}
}

View file

@ -1,6 +1,7 @@
package ru.gravit.launcher.console; package ru.gravit.launcher.console;
import ru.gravit.launcher.managers.ConsoleManager; import ru.gravit.launcher.managers.ConsoleManager;
import ru.gravit.launcher.managers.SettingsManager;
import ru.gravit.utils.command.Command; import ru.gravit.utils.command.Command;
import ru.gravit.utils.helper.LogHelper; import ru.gravit.utils.helper.LogHelper;
@ -22,6 +23,8 @@ public void invoke(String... args) throws Exception {
LogHelper.info("Unlock successful"); LogHelper.info("Unlock successful");
ConsoleManager.unlock(); ConsoleManager.unlock();
ConsoleManager.handler.unregisterCommand("unlock"); ConsoleManager.handler.unregisterCommand("unlock");
LogHelper.info("Write unlock key");
SettingsManager.settings.consoleUnlockKey = args[0];
} else { } else {
LogHelper.error("Unlock key incorrect"); LogHelper.error("Unlock key incorrect");
} }

View file

@ -0,0 +1,51 @@
package ru.gravit.launcher.console.store;
import ru.gravit.launcher.NewLauncherSettings;
import ru.gravit.launcher.managers.SettingsManager;
import ru.gravit.utils.command.Command;
import ru.gravit.utils.helper.LogHelper;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class CopyStoreDirCommand extends Command {
@Override
public String getArgsDescription() {
return "[index] [overwrite(true/false)]";
}
@Override
public String getUsageDescription() {
return "Copy dir in GravitLauncherStore";
}
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 2);
int ind = 1;
int index = Integer.valueOf(args[0]);
boolean overwrite = Boolean.valueOf(args[1]);
for(NewLauncherSettings.HashedStoreEntry e : SettingsManager.settings.lastHDirs)
{
if(ind == index)
{
LogHelper.info("Copy [%d] FullPath: %s name: %s", ind, e.fullPath, e.name);
Path path = Paths.get(e.fullPath);
if(!Files.isDirectory(path))
{
LogHelper.error("Directory %s not found", path.toAbsolutePath().toString());
return;
}
Path target = Paths.get(SettingsManager.settings.updatesDirPath).resolve(e.name);
if(Files.exists(target) && !overwrite)
{
LogHelper.error("Directory %s found, flag overwrite not found", target.toAbsolutePath().toString());
return;
}
Files.copy(path, target);
}
ind++;
}
}
}

View file

@ -0,0 +1,51 @@
package ru.gravit.launcher.console.store;
import ru.gravit.launcher.NewLauncherSettings;
import ru.gravit.launcher.managers.SettingsManager;
import ru.gravit.utils.command.Command;
import ru.gravit.utils.helper.LogHelper;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class LinkStoreDirCommand extends Command {
@Override
public String getArgsDescription() {
return "[index]";
}
@Override
public String getUsageDescription() {
return "Create symlink to GravitLauncherStore directory";
}
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
int ind = 1;
int index = Integer.valueOf(args[0]);
for(NewLauncherSettings.HashedStoreEntry e : SettingsManager.settings.lastHDirs)
{
if(ind == index)
{
LogHelper.info("Copy [%d] FullPath: %s name: %s", ind, e.fullPath, e.name);
Path path = Paths.get(e.fullPath);
if(!Files.isDirectory(path))
{
LogHelper.error("Directory %s not found", path.toAbsolutePath().toString());
return;
}
Path target = Paths.get(SettingsManager.settings.updatesDirPath).resolve(e.name);
if(Files.exists(target))
{
LogHelper.error("Directory %s already exists", target.toAbsolutePath().toString());
return;
}
Files.createSymbolicLink(path, target);
}
ind++;
}
}
}

View file

@ -0,0 +1,28 @@
package ru.gravit.launcher.console.store;
import ru.gravit.launcher.NewLauncherSettings;
import ru.gravit.launcher.managers.SettingsManager;
import ru.gravit.utils.command.Command;
import ru.gravit.utils.helper.LogHelper;
public class StoreListCommand extends Command {
@Override
public String getArgsDescription() {
return null;
}
@Override
public String getUsageDescription() {
return "List GravitLauncherStore";
}
@Override
public void invoke(String... args) throws Exception {
int ind = 1;
for(NewLauncherSettings.HashedStoreEntry e : SettingsManager.settings.lastHDirs)
{
LogHelper.info("[%d] FullPath: %s name: %s", ind, e.fullPath, e.name);
ind++;
}
}
}

View file

@ -1,8 +1,13 @@
package ru.gravit.launcher.managers; package ru.gravit.launcher.managers;
import ru.gravit.launcher.Launcher;
import ru.gravit.launcher.console.FeatureCommand;
import ru.gravit.launcher.console.UnlockCommand; import ru.gravit.launcher.console.UnlockCommand;
import ru.gravit.launcher.console.admin.ExecCommand; import ru.gravit.launcher.console.admin.ExecCommand;
import ru.gravit.launcher.console.admin.LogListenerCommand; import ru.gravit.launcher.console.admin.LogListenerCommand;
import ru.gravit.launcher.console.store.CopyStoreDirCommand;
import ru.gravit.launcher.console.store.LinkStoreDirCommand;
import ru.gravit.launcher.console.store.StoreListCommand;
import ru.gravit.utils.command.BaseCommandCategory; import ru.gravit.utils.command.BaseCommandCategory;
import ru.gravit.utils.command.CommandHandler; import ru.gravit.utils.command.CommandHandler;
import ru.gravit.utils.command.JLineCommandHandler; import ru.gravit.utils.command.JLineCommandHandler;
@ -15,10 +20,14 @@
import ru.gravit.utils.helper.LogHelper; import ru.gravit.utils.helper.LogHelper;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class ConsoleManager { public class ConsoleManager {
public static CommandHandler handler; public static CommandHandler handler;
public static Thread thread; public static Thread thread;
public static boolean isConsoleUnlock = false;
public static void initConsole() throws IOException { public static void initConsole() throws IOException {
CommandHandler localCommandHandler; CommandHandler localCommandHandler;
@ -46,14 +55,21 @@ public static void registerCommands() {
} }
public static boolean checkUnlockKey(String key) { public static boolean checkUnlockKey(String key) {
return true; return key.equals(Launcher.getConfig().oemUnlockKey);
} }
public static void unlock() { public static void unlock() {
handler.registerCommand("debug", new DebugCommand()); handler.registerCommand("debug", new DebugCommand());
handler.registerCommand("feature", new FeatureCommand());
BaseCommandCategory admin = new BaseCommandCategory(); BaseCommandCategory admin = new BaseCommandCategory();
admin.registerCommand("exec", new ExecCommand()); admin.registerCommand("exec", new ExecCommand());
admin.registerCommand("logListen", new LogListenerCommand()); admin.registerCommand("logListen", new LogListenerCommand());
handler.registerCategory(new CommandHandler.Category(admin, "admin", "Server admin commands")); handler.registerCategory(new CommandHandler.Category(admin, "admin", "Server admin commands"));
BaseCommandCategory store = new BaseCommandCategory();
store.registerCommand("storeList", new StoreListCommand());
store.registerCommand("copyStoreDir", new CopyStoreDirCommand());
store.registerCommand("linkStoreDir", new LinkStoreDirCommand());
handler.registerCategory(new CommandHandler.Category(admin, "store", "Store admin commands"));
isConsoleUnlock = true;
} }
} }

View file

@ -8,6 +8,7 @@
import ru.gravit.launcher.serialize.HInput; import ru.gravit.launcher.serialize.HInput;
import ru.gravit.launcher.serialize.HOutput; import ru.gravit.launcher.serialize.HOutput;
import ru.gravit.utils.helper.IOHelper; import ru.gravit.utils.helper.IOHelper;
import ru.gravit.utils.helper.LogHelper;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@ -24,6 +25,9 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
String fullPath = input.readString(1024); String fullPath = input.readString(1024);
HashedDir dir = new HashedDir(input); HashedDir dir = new HashedDir(input);
settings.lastHDirs.add(new NewLauncherSettings.HashedStoreEntry(dir, dirName, fullPath)); settings.lastHDirs.add(new NewLauncherSettings.HashedStoreEntry(dir, dirName, fullPath));
} catch (IOException e)
{
LogHelper.error("Skip file %s exception: %s", file.toAbsolutePath().toString(), e.getMessage());
} }
return super.visitFile(file, attrs); return super.visitFile(file, attrs);
} }
@ -57,6 +61,14 @@ public void setConfig(NewLauncherSettings config) {
settings = config; settings = config;
if (settings.updatesDirPath != null) if (settings.updatesDirPath != null)
settings.updatesDir = Paths.get(settings.updatesDirPath); settings.updatesDir = Paths.get(settings.updatesDirPath);
if(settings.consoleUnlockKey != null && !ConsoleManager.isConsoleUnlock)
{
if(ConsoleManager.checkUnlockKey(settings.consoleUnlockKey))
{
ConsoleManager.unlock();
LogHelper.info("Console auto unlocked");
}
}
} }
@LauncherAPI @LauncherAPI
@ -69,6 +81,7 @@ public void loadHDirStore(Path storePath) throws IOException {
public void saveHDirStore(Path storeProjectPath) throws IOException { public void saveHDirStore(Path storeProjectPath) throws IOException {
Files.createDirectories(storeProjectPath); Files.createDirectories(storeProjectPath);
for (NewLauncherSettings.HashedStoreEntry e : settings.lastHDirs) { for (NewLauncherSettings.HashedStoreEntry e : settings.lastHDirs) {
if(!e.needSave) continue;
Path file = storeProjectPath.resolve(e.name.concat(".bin")); Path file = storeProjectPath.resolve(e.name.concat(".bin"));
if (!Files.exists(file)) Files.createFile(file); if (!Files.exists(file)) Files.createFile(file);
try (HOutput output = new HOutput(IOHelper.newOutput(file))) { try (HOutput output = new HOutput(IOHelper.newOutput(file))) {

View file

@ -8,6 +8,7 @@ public class AutogenConfig {
private boolean isInitModules; private boolean isInitModules;
public String guardType; public String guardType;
public String secretKeyClient; public String secretKeyClient;
public String oemUnlockKey;
public String guardLicenseName; public String guardLicenseName;
public String guardLicenseKey; public String guardLicenseKey;
public String guardLicenseEncryptKey; public String guardLicenseEncryptKey;

View file

@ -25,6 +25,7 @@ public static AutogenConfig getAutogenConfig() {
public final String projectname; public final String projectname;
public final int clientPort; public final int clientPort;
public String secretKeyClient; public String secretKeyClient;
public String oemUnlockKey;
@LauncherAPI @LauncherAPI
public final RSAPublicKey publicKey; public final RSAPublicKey publicKey;
@ -45,6 +46,7 @@ public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException
projectname = config.projectname; projectname = config.projectname;
clientPort = config.clientPort; clientPort = config.clientPort;
secretKeyClient = config.secretKeyClient; secretKeyClient = config.secretKeyClient;
oemUnlockKey = config.oemUnlockKey;
isWarningMissArchJava = config.isWarningMissArchJava; isWarningMissArchJava = config.isWarningMissArchJava;
guardLicenseEncryptKey = config.guardLicenseEncryptKey; guardLicenseEncryptKey = config.guardLicenseEncryptKey;

View file

@ -18,6 +18,8 @@
import java.net.URL; import java.net.URL;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ListDownloader { public class ListDownloader {
@FunctionalInterface @FunctionalInterface
@ -49,13 +51,36 @@ public void download(String base, List<DownloadTask> applies, Path dstDirFile, D
for (DownloadTask apply : applies) { for (DownloadTask apply : applies) {
URI u = new URL(base.concat(IOHelper.urlEncode(apply.apply).replace("%2F", "/"))).toURI(); URI u = new URL(base.concat(IOHelper.urlEncode(apply.apply).replace("%2F", "/"))).toURI();
callback.stateChanged(apply.apply, 0L, apply.size); callback.stateChanged(apply.apply, 0L, apply.size);
LogHelper.debug("Download URL: %s", u.toString()); Path targetPath = dstDirFile.resolve(apply.apply);
LogHelper.debug("Download URL: %s to file %s dir: %s", u.toString(), targetPath.toAbsolutePath().toString(), dstDirFile.toAbsolutePath().toString());
if (get == null) get = new HttpGet(u); if (get == null) get = new HttpGet(u);
else { else {
get.reset(); get.reset();
get.setURI(u); get.setURI(u);
} }
httpclient.execute(get, new FileDownloadResponseHandler(dstDirFile.resolve(apply.apply), apply, callback, totalCallback)); httpclient.execute(get, new FileDownloadResponseHandler(targetPath, apply, callback, totalCallback, false));
}
}
}
public void downloadZip(String base, Path dstDirFile, DownloadCallback callback, DownloadTotalCallback totalCallback) throws IOException, URISyntaxException {
/*try (CloseableHttpClient httpclient = HttpClients.custom()
.setRedirectStrategy(new LaxRedirectStrategy())
.build()) {
HttpGet get;
URI u = new URL(base).toURI();
LogHelper.debug("Download ZIP URL: %s", u.toString());
get = new HttpGet(u);
httpclient.execute(get, new FileDownloadResponseHandler(dstDirFile, callback, totalCallback, true));
}*/
try (ZipInputStream input = IOHelper.newZipInput(new URL(base))) {
for (ZipEntry entry = input.getNextEntry(); entry != null; entry = input.getNextEntry()) {
if (entry.isDirectory())
continue; // Skip directories
// Unpack entry
String name = entry.getName();
LogHelper.subInfo("Downloading file: '%s'", name);
Path fileName = IOHelper.toPath(name);
transfer(input, dstDirFile.resolve(fileName), fileName.toString(), entry.getSize(), callback, totalCallback);
} }
} }
} }
@ -78,24 +103,62 @@ static class FileDownloadResponseHandler implements ResponseHandler<Path> {
private final DownloadTask task; private final DownloadTask task;
private final DownloadCallback callback; private final DownloadCallback callback;
private final DownloadTotalCallback totalCallback; private final DownloadTotalCallback totalCallback;
private final boolean zip;
public FileDownloadResponseHandler(Path target) { public FileDownloadResponseHandler(Path target) {
this.target = target; this.target = target;
this.task = null; this.task = null;
this.zip = false;
callback = null; callback = null;
totalCallback = null; totalCallback = null;
} }
public FileDownloadResponseHandler(Path target, DownloadTask task, DownloadCallback callback, DownloadTotalCallback totalCallback) { public FileDownloadResponseHandler(Path target, DownloadTask task, DownloadCallback callback, DownloadTotalCallback totalCallback, boolean zip) {
this.target = target; this.target = target;
this.task = task; this.task = task;
this.callback = callback; this.callback = callback;
this.totalCallback = totalCallback; this.totalCallback = totalCallback;
this.zip = zip;
}
public FileDownloadResponseHandler(Path target, DownloadCallback callback, DownloadTotalCallback totalCallback, boolean zip) {
this.target = target;
this.task = null;
this.callback = callback;
this.totalCallback = totalCallback;
this.zip = zip;
} }
@Override @Override
public Path handleResponse(HttpResponse response) throws IOException { public Path handleResponse(HttpResponse response) throws IOException {
InputStream source = response.getEntity().getContent(); InputStream source = response.getEntity().getContent();
if(zip)
{
try(ZipInputStream input = IOHelper.newZipInput(source))
{
ZipEntry entry = input.getNextEntry();
while(entry != null)
{
if(entry.isDirectory())
{
entry = input.getNextEntry();
continue;
}
long size = entry.getSize();
String filename = entry.getName();
Path target = this.target.resolve(filename);
if(callback != null)
{
callback.stateChanged(entry.getName(), 0, entry.getSize());
}
LogHelper.dev("Resolved filename %s to %s", filename, target.toAbsolutePath().toString());
transfer(source, target, filename, size, callback, totalCallback);
entry = input.getNextEntry();
}
}
return null;
}
if (callback != null && task != null) { if (callback != null && task != null) {
callback.stateChanged(task.apply, 0, task.size); callback.stateChanged(task.apply, 0, task.size);
transfer(source, this.target, task.apply, task.size, callback, totalCallback); transfer(source, this.target, task.apply, task.size, callback, totalCallback);

View file

@ -9,6 +9,8 @@ public class UpdateRequestEvent extends RequestEvent {
public HashedDir hdir; public HashedDir hdir;
@LauncherNetworkAPI @LauncherNetworkAPI
public String url; public String url;
@LauncherNetworkAPI
public boolean zip;
@Override @Override
public String getType() { public String getType() {
@ -17,10 +19,18 @@ public String getType() {
public UpdateRequestEvent(HashedDir hdir) { public UpdateRequestEvent(HashedDir hdir) {
this.hdir = hdir; this.hdir = hdir;
this.zip = false;
} }
public UpdateRequestEvent(HashedDir hdir, String url) { public UpdateRequestEvent(HashedDir hdir, String url) {
this.hdir = hdir; this.hdir = hdir;
this.url = url; this.url = url;
this.zip = false;
}
public UpdateRequestEvent(HashedDir hdir, String url, boolean zip) {
this.hdir = hdir;
this.url = url;
this.zip = zip;
} }
} }

View file

@ -27,6 +27,20 @@
import java.util.Objects; import java.util.Objects;
public final class UpdateRequest extends Request<UpdateRequestEvent> implements RequestInterface { public final class UpdateRequest extends Request<UpdateRequestEvent> implements RequestInterface {
public interface UpdateController
{
void preUpdate(UpdateRequest request, UpdateRequestEvent e) throws IOException;
void preDiff(UpdateRequest request, UpdateRequestEvent e) throws IOException;
void postDiff(UpdateRequest request, UpdateRequestEvent e,HashedDir.Diff diff) throws IOException;
void preDownload(UpdateRequest request, UpdateRequestEvent e, List<ListDownloader.DownloadTask> adds) throws IOException;
void postDownload(UpdateRequest request, UpdateRequestEvent e) throws IOException;
void postUpdate(UpdateRequest request, UpdateRequestEvent e) throws IOException;
}
private static UpdateController controller;
public static void setController(UpdateController controller) {
UpdateRequest.controller = controller;
}
@Override @Override
public String getType() { public String getType() {
@ -170,15 +184,22 @@ public double getTotalSizeMiB() {
public UpdateRequestEvent requestDo(StandartClientWebSocketService service) throws Exception { public UpdateRequestEvent requestDo(StandartClientWebSocketService service) throws Exception {
LogHelper.debug("Start update request"); LogHelper.debug("Start update request");
UpdateRequestEvent e = (UpdateRequestEvent) service.sendRequest(this); UpdateRequestEvent e = (UpdateRequestEvent) service.sendRequest(this);
if(controller != null) controller.preUpdate(this, e);
LogHelper.debug("Start update"); LogHelper.debug("Start update");
Launcher.profile.pushOptionalFile(e.hdir, !Launcher.profile.isUpdateFastCheck()); Launcher.profile.pushOptionalFile(e.hdir, !Launcher.profile.isUpdateFastCheck());
if(controller != null) controller.preDiff(this, e);
HashedDir.Diff diff = e.hdir.diff(localDir, matcher); HashedDir.Diff diff = e.hdir.diff(localDir, matcher);
if(controller != null) controller.postDiff(this, e, diff);
final List<ListDownloader.DownloadTask> adds = new ArrayList<>(); final List<ListDownloader.DownloadTask> adds = new ArrayList<>();
if(controller != null) controller.preDownload(this, e, adds);
diff.mismatch.walk(IOHelper.CROSS_SEPARATOR, (path, name, entry) -> { diff.mismatch.walk(IOHelper.CROSS_SEPARATOR, (path, name, entry) -> {
if (entry.getType().equals(HashedEntry.Type.FILE)) { if (entry.getType().equals(HashedEntry.Type.FILE)) {
HashedFile file = (HashedFile) entry; if(!entry.flag)
totalSize += file.size; {
adds.add(new ListDownloader.DownloadTask(path, file.size)); HashedFile file = (HashedFile) entry;
totalSize += file.size;
adds.add(new ListDownloader.DownloadTask(path, file.size));
}
} else if (entry.getType().equals(HashedEntry.Type.DIR)) { } else if (entry.getType().equals(HashedEntry.Type.DIR)) {
try { try {
Files.createDirectories(dir.resolve(path)); Files.createDirectories(dir.resolve(path));
@ -186,13 +207,24 @@ public UpdateRequestEvent requestDo(StandartClientWebSocketService service) thro
LogHelper.error(ex); LogHelper.error(ex);
} }
} }
return HashedDir.WalkAction.CONTINUE;
}); });
totalSize = diff.mismatch.size(); totalSize = diff.mismatch.size();
startTime = Instant.now(); startTime = Instant.now();
updateState("UnknownFile", 0L, 100); updateState("UnknownFile", 0L, 100);
ListDownloader listDownloader = new ListDownloader(); ListDownloader listDownloader = new ListDownloader();
listDownloader.download(e.url, adds, dir, this::updateState, (add) -> totalDownloaded += add); LogHelper.info("Download %s to %s", dirName, dir.toAbsolutePath().toString());
if(e.zip && !adds.isEmpty())
{
listDownloader.downloadZip(e.url, dir, this::updateState, (add) -> totalDownloaded += add);
}
else
{
listDownloader.download(e.url, adds, dir, this::updateState, (add) -> totalDownloaded += add);
}
if(controller != null) controller.postDownload(this, e);
deleteExtraDir(dir, diff.extra, diff.extra.flag); deleteExtraDir(dir, diff.extra, diff.extra.flag);
if(controller != null) controller.postUpdate(this, e);
LogHelper.debug("Update success"); LogHelper.debug("Update success");
return e; return e;
} }
@ -201,6 +233,11 @@ public UpdateRequestEvent requestDo(StandartClientWebSocketService service) thro
@LauncherNetworkAPI @LauncherNetworkAPI
private final String dirName; private final String dirName;
private transient final Path dir; private transient final Path dir;
public Path getDir() {
return dir;
}
private transient final FileNameMatcher matcher; private transient final FileNameMatcher matcher;
private transient final boolean digest; private transient final boolean digest;

View file

@ -5,10 +5,12 @@
import ru.gravit.utils.helper.LogHelper; import ru.gravit.utils.helper.LogHelper;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class WaitEventHandler implements ClientWebSocketService.EventHandler { public class WaitEventHandler implements ClientWebSocketService.EventHandler {
public HashSet<ResultEvent> requests = new HashSet<>(); public Set<ResultEvent> requests = ConcurrentHashMap.newKeySet();
@Override @Override
public void process(ResultInterface result) { public void process(ResultInterface result) {

View file

@ -6,6 +6,8 @@
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import ru.gravit.utils.helper.LogHelper; import ru.gravit.utils.helper.LogHelper;
import java.util.concurrent.TimeUnit;
public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> { public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> {
private final WebSocketClientHandshaker handshaker; private final WebSocketClientHandshaker handshaker;
@ -30,6 +32,9 @@ public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
public void channelActive(final ChannelHandlerContext ctx) throws Exception { public void channelActive(final ChannelHandlerContext ctx) throws Exception {
handshaker.handshake(ctx.channel()); handshaker.handshake(ctx.channel());
clientJSONPoint.onOpen(); clientJSONPoint.onOpen();
ctx.executor().schedule(() -> {
ctx.channel().writeAndFlush(new PingWebSocketFrame());
}, 20L, TimeUnit.SECONDS);
} }
@Override @Override

View file

@ -338,31 +338,44 @@ public void write(HOutput output) throws IOException {
} }
} }
public void walk(CharSequence separator, WalkCallback callback) { public void walk(CharSequence separator, WalkCallback callback) throws IOException {
String append = ""; String append = "";
walk(append, separator, callback, true); walk(append, separator, callback, true);
} }
public enum WalkAction
{
STOP, CONTINUE
}
@FunctionalInterface @FunctionalInterface
public interface WalkCallback { public interface WalkCallback {
void walked(String path, String name, HashedEntry entry); WalkAction walked(String path, String name, HashedEntry entry) throws IOException;
} }
private void walk(String append, CharSequence separator, WalkCallback callback, boolean noSeparator) { private WalkAction walk(String append, CharSequence separator, WalkCallback callback, boolean noSeparator) throws IOException {
for (Map.Entry<String, HashedEntry> entry : map.entrySet()) { for (Map.Entry<String, HashedEntry> entry : map.entrySet()) {
HashedEntry e = entry.getValue(); HashedEntry e = entry.getValue();
if (e.getType() == Type.FILE) { if (e.getType() == Type.FILE) {
if (noSeparator) if (noSeparator)
callback.walked(append + entry.getKey(), entry.getKey(), e); {
WalkAction a = callback.walked(append + entry.getKey(), entry.getKey(), e);
if(a == WalkAction.STOP) return a;
}
else else
callback.walked(append + separator + entry.getKey(), entry.getKey(), e); {
WalkAction a = callback.walked(append + separator + entry.getKey(), entry.getKey(), e);
if(a == WalkAction.STOP) return a;
}
} else { } else {
String newAppend; String newAppend;
if (noSeparator) newAppend = append + entry.getKey(); if (noSeparator) newAppend = append + entry.getKey();
else newAppend = append + separator + entry.getKey(); else newAppend = append + separator + entry.getKey();
callback.walked(newAppend, entry.getKey(), e); WalkAction a = callback.walked(newAppend, entry.getKey(), e);
((HashedDir) e).walk(newAppend, separator, callback, false); if(a == WalkAction.STOP) return a;
a = ((HashedDir) e).walk(newAppend, separator, callback, false);
if(a == WalkAction.STOP) return a;
} }
} }
return WalkAction.CONTINUE;
} }
} }

View file

@ -17,8 +17,8 @@ public final class Version {
public final Type release; public final Type release;
public static final int MAJOR = 5; public static final int MAJOR = 5;
public static final int MINOR = 0; public static final int MINOR = 0;
public static final int PATCH = 0; public static final int PATCH = 1;
public static final int BUILD = 7; public static final int BUILD = 1;
public static final Version.Type RELEASE = Version.Type.STABLE; public static final Version.Type RELEASE = Version.Type.STABLE;
@LauncherAPI @LauncherAPI

View file

@ -75,6 +75,7 @@ public String readLine() throws IOException {
try { try {
return reader.readLine(); return reader.readLine();
} catch (UserInterruptException e) { } catch (UserInterruptException e) {
System.exit(0);
return null; return null;
} }
} }

View file

@ -113,7 +113,10 @@ public static void debug(String format, Object... args) {
@LauncherAPI @LauncherAPI
public static void dev(String format, Object... args) { public static void dev(String format, Object... args) {
dev(String.format(format, args)); if(isDevEnabled())
{
dev(String.format(format, args));
}
} }
@LauncherAPI @LauncherAPI