summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assets/i18n/en_us.json29
-rw-r--r--assets/i18n/ru_ru.json29
-rw-r--r--core/src/kz/ilotterytea/maxon/MaxonConstants.java9
-rw-r--r--core/src/kz/ilotterytea/maxon/MaxonGame.java179
-rw-r--r--core/src/kz/ilotterytea/maxon/localization/LineId.java9
-rw-r--r--core/src/kz/ilotterytea/maxon/screens/MenuScreen.java206
-rw-r--r--core/src/kz/ilotterytea/maxon/session/SessionClient.java323
7 files changed, 648 insertions, 136 deletions
diff --git a/assets/i18n/en_us.json b/assets/i18n/en_us.json
index ccbacb2..458113f 100644
--- a/assets/i18n/en_us.json
+++ b/assets/i18n/en_us.json
@@ -1,14 +1,11 @@
{
"updater.info": "A new update is available!",
-
"menu.new_game": "New Game",
"menu.continue": "CONTINUE",
"menu.reset": "RESET",
-
"sounds.title": "Mixer",
"sounds.music": "Music",
"sounds.sfx": "SFX",
-
"credits.title": "Those who contributed",
"credits.maxon": "Maxon himself",
"credits.developer": "Developer",
@@ -17,64 +14,54 @@
"credits.idea": "Idea dispenser",
"credits.tester": "Tester",
"credits.moral": "Moral supporter",
-
+ "login.title": "Sign-in(tm) account",
+ "login.username": "Username",
+ "login.password": "Password",
+ "login.register": "Need account?",
+ "login.send": "Login",
+ "login.processing": "Authorising...",
+ "login.button": "Sign in...",
+ "login.authorised": "Logged in as %s",
"giftbox.open": "You got",
-
"minigame.slots.nothing": "ALMOST WON...",
"minigame.slots.prize": "YOU WON %s P$",
"minigame.slots.bet": "BET",
"minigame.slots.spin_button": "SPIN",
"minigame.slots.exit_button": " X ",
-
"store.title": "Store",
"store.buy": "Buy",
"store.sell": "Sell",
"store.x1": "1x",
"store.x10": "10x",
"store.pet_locked": "Pet Maxon more to unlock it...",
-
"pet.bror.name": "Sleepy Bror",
"pet.bror.desc": "A falling asleep Bror will help you to pet Maxon almost to besvimers",
-
"pet.sandwich.name": "The Sandwich Cat",
"pet.sandwich.desc": "Even though his head is shielded from the light by bread, he can still to pet Maxon by his cheeks.",
-
"pet.manlooshka.name": "Manlooshka",
"pet.manlooshka.desc": "rrrrr",
-
"pet.thirsty.name": "The Thirsty Cat",
"pet.thirsty.desc": "Every time the kitty drinks water, drops of spilled water fall on the screen and pet Maxon's dry cheeks.",
-
"pet.furios.name": "The Furios Cat",
"pet.furios.desc": "Petting FURIOSLY !!!",
-
"pet.tvcat.name": "TV cat",
"pet.tvcat.desc": "just a funny cat playing with tv",
-
"pet.progcat.name": "Programmer cat",
"pet.progcat.desc": "Using his programming skills, he developed software to pet Maxon more efficiently.",
-
"pet.screamcat.name": "Screaming cat",
"pet.screamcat.desc": "His scream is so loud, that it shakes Maxon's cheeks.",
-
"pet.hellcat.name": "Hellcat",
"pet.hellcat.desc": "Petting Maxon with POWER OF HELL.",
-
"pet.lurker.name": "Lurking cat",
"pet.lurker.desc": "Finds the best spots to pet Maxon in.",
-
"pet.piano.name": "Piano cat",
"pet.piano.desc": "Gives best pettings with his trained paws.",
-
"pet.bee.name": "Bee cat",
"pet.bee.desc": "Collects pollen from Maxon's cheeks.",
-
"pet.busy.name": "Business cat",
"pet.busy.desc": "Pets Maxon with help of all his influence.",
-
"pet.aeae.name": "Aeae",
"pet.aeae.desc": "back and forth, back and forth.",
-
"pet.succat.name": "Sucking cat",
"pet.succat.desc": "Maskot. Increases amount of petting with his sucking power."
} \ No newline at end of file
diff --git a/assets/i18n/ru_ru.json b/assets/i18n/ru_ru.json
index 2d092d6..1d8dc73 100644
--- a/assets/i18n/ru_ru.json
+++ b/assets/i18n/ru_ru.json
@@ -1,14 +1,11 @@
{
"updater.info": "Доступно новое обновление!",
-
"menu.new_game": "Новая игра",
"menu.continue": "ПРОДОЛЖИТЬ",
"menu.reset": "СБРОСИТЬ",
-
"sounds.title": "Микшер",
"sounds.music": "Музыка",
"sounds.sfx": "SFX",
-
"credits.title": "Те, кто внёс вклад",
"credits.maxon": "Максон",
"credits.developer": "Разработчик",
@@ -17,64 +14,54 @@
"credits.idea": "Раздатчик идей",
"credits.tester": "Тестер",
"credits.moral": "Моральная помощь",
-
+ "login.title": "Sign-in(tm) аккаунт",
+ "login.username": "Имя пользователя",
+ "login.password": "Пароль",
+ "login.register": "Нужен аккаунт?",
+ "login.send": "Зайти",
+ "login.processing": "Авторизируюсь...",
+ "login.button": "Вход",
+ "login.authorised": "Зашёл как %s",
"giftbox.open": "Ты получил",
-
"minigame.slots.nothing": "ПОЧТИ ВЫИГРАЛ...",
"minigame.slots.prize": "ТЫ ВЫИГРАЛ %s P$",
"minigame.slots.bet": "СТАВКА",
"minigame.slots.spin_button": "КРУТАНУТЬ",
"minigame.slots.exit_button": " X ",
-
"store.title": "Магазин",
"store.buy": "Купить",
"store.sell": "Продать",
"store.x1": "1x",
"store.x10": "10x",
"store.pet_locked": "Гладь Махона больше, чтобы открыть...",
-
"pet.bror.name": "Cонный Брор",
"pet.bror.desc": "Засыпающий Брор поможет тебе гладить Максона практически до потери сознания.",
-
"pet.sandwich.name": "Кот-бутерброд",
"pet.sandwich.desc": "Даже, когда его голова накрыта хлебом от солнечного света, он всё ещё может гладить Максона за его щёчки.",
-
"pet.manlooshka.name": "Манлушка",
"pet.manlooshka.desc": "*ррррр*",
-
"pet.thirsty.name": "Жаждущая кошка",
"pet.thirsty.desc": "Каждый раз, когда он пьёт воду, капли пролитой воды падают на экран и гладят Максона за его сухие щёчки.",
-
"pet.furios.name": "Яростный кот",
"pet.furios.desc": "ласкает НЕИСТОВО !!!",
-
"pet.tvcat.name": "Телевизионный котик",
"pet.tvcat.desc": "он сильный",
-
"pet.progcat.name": "Кот-программист",
"pet.progcat.desc": "Используя свои навыки программирования, он написал софт для более эффективного поглаживания Максона.",
-
"pet.screamcat.name": "Кричащий кот",
"pet.screamcat.desc": "Его крик настолько громкий, что способен содрогать щёчки Максона.",
-
"pet.hellcat.name": "Адский кот",
"pet.hellcat.desc": "Ласкает Максона силой ПРЕИСПОДНЕЙ.",
-
"pet.lurker.name": "Луркающий котик",
"pet.lurker.desc": "Выявляет самые выгодные места для почесывания Максона.",
-
"pet.piano.name": "Котик-пианист",
"pet.piano.desc": "Своими натренированными лапками дарит самые лучшие печесушки.",
-
"pet.bee.name": "Пчелокот",
"pet.bee.desc": "Собирает пыльцу с щёчек Максона, попутно поглаживая их.",
-
"pet.busy.name": "Деловой кот",
"pet.busy.desc": "Используя все свои бизнес-связи и своих работников, гладит Максона.",
-
"pet.aeae.name": "Аеае",
"pet.aeae.desc": "туда-сюда, туда-сюда.",
-
"pet.succat.name": "Сосущий кот",
"pet.succat.desc": "Талисман. Своими сосательными навыками увеличивает количество поглаживаний."
} \ No newline at end of file
diff --git a/core/src/kz/ilotterytea/maxon/MaxonConstants.java b/core/src/kz/ilotterytea/maxon/MaxonConstants.java
index 5139ccc..cab66f4 100644
--- a/core/src/kz/ilotterytea/maxon/MaxonConstants.java
+++ b/core/src/kz/ilotterytea/maxon/MaxonConstants.java
@@ -18,6 +18,7 @@ public class MaxonConstants {
public static final String GAME_APP_PACKAGE = "kz.ilotterytea." + GAME_APP_ID;
public static final String GAME_APP_URL;
public static final String GAME_VERSION = "1.0.1";
+ public static final Integer GAME_PROTOCOL = 1;
public static final String GAME_MAIN_DEVELOPER = "ilotterytea";
public static final List<Triple<String, String, Integer>> GAME_DEVELOPERS = Arrays.asList(
@@ -55,6 +56,12 @@ public class MaxonConstants {
public static final DecimalFormat DECIMAL_FORMAT2 = new DecimalFormat("###,###");
public static final String GAME_VERSIONS_FILE_URL = "https://assets.ilotterytea.kz/maxon/versions.json";
+ public static final String IDENTITY_BASE_URL = "https://id.ilotterytea.kz";
+ public static final String IDENTITY_INVALIDATE_URL = IDENTITY_BASE_URL + "/invalidate";
+ public static final String IDENTITY_VALIDATE_URL = IDENTITY_BASE_URL + "/validate";
+ public static final String IDENTITY_REFRESH_URL = IDENTITY_BASE_URL + "/refresh";
+ public static final String IDENTITY_AUTHENTICATION_URL = IDENTITY_BASE_URL + "/authenticate";
+ public static final String IDENTITY_REGISTRATION_URL = IDENTITY_BASE_URL;
public static final long DISCORD_APPLICATION_ID = 1051092609659052062L;
@@ -68,7 +75,7 @@ public class MaxonConstants {
for (int y = 0; y < checkers; y++) {
for (int x = 0; x < checkers; x++) {
- if ((x + y) % 2 == 0){
+ if ((x + y) % 2 == 0) {
pixmap.setColor(Color.MAGENTA);
} else {
pixmap.setColor(Color.BLACK);
diff --git a/core/src/kz/ilotterytea/maxon/MaxonGame.java b/core/src/kz/ilotterytea/maxon/MaxonGame.java
index 63de3e8..b53c510 100644
--- a/core/src/kz/ilotterytea/maxon/MaxonGame.java
+++ b/core/src/kz/ilotterytea/maxon/MaxonGame.java
@@ -8,93 +8,100 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import kz.ilotterytea.maxon.localization.LocalizationManager;
import kz.ilotterytea.maxon.pets.PetManager;
import kz.ilotterytea.maxon.screens.SplashScreen;
+import kz.ilotterytea.maxon.session.SessionClient;
import kz.ilotterytea.maxon.utils.GameUpdater;
public class MaxonGame extends Game {
- public SpriteBatch batch;
- public AssetManager assetManager;
- public Preferences prefs;
-
- private LocalizationManager locale;
- private PetManager petManager;
-
- private DiscordActivityClient discordActivityClient;
-
- private static MaxonGame instance;
-
- public static MaxonGame getInstance() {
- if (instance == null) {
- instance = new MaxonGame();
- }
- return instance;
- }
-
- public PetManager getPetManager() {
- return petManager;
- }
-
- public DiscordActivityClient getDiscordActivityClient() {
- return discordActivityClient;
- }
-
- public LocalizationManager getLocale() {
- return locale;
- }
-
- public void setLocale(LocalizationManager locale) {
- this.locale = locale;
- }
-
- @Override
- public void create () {
- // Check the latest version
- new GameUpdater().checkLatestUpdate();
-
- batch = new SpriteBatch();
- prefs = Gdx.app.getPreferences(MaxonConstants.GAME_APP_PACKAGE);
- locale = new LocalizationManager(Gdx.files.internal("i18n/" + prefs.getString("lang", "en_us") + ".json"));
-
- Gdx.graphics.setVSync(prefs.getBoolean("vsync", true));
-
- if (prefs.getBoolean("fullscreen", false)) {
- Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
- } else if (
- prefs.contains("width") ||
- prefs.contains("height")
- ) {
- int width = prefs.getInteger("width", 800);
-
- if (width < 800) {
- width = 800;
- prefs.putInteger("width", width);
- }
-
- int height = prefs.getInteger("height", 600);
-
- if (height < 600) {
- height = 600;
- prefs.putInteger("height", height);
- }
-
- prefs.flush();
- Gdx.graphics.setWindowedMode(width, height);
- }
-
- assetManager = new AssetManager();
- petManager = new PetManager(assetManager);
-
- discordActivityClient = new DiscordActivityClient();
-
- this.setScreen(new SplashScreen());
- }
-
- @Override
- public void dispose () {
- batch.dispose();
- for (String name : assetManager.getAssetNames()) {
- assetManager.unload(name);
- }
- assetManager.dispose();
- discordActivityClient.dispose();
- }
+ public SpriteBatch batch;
+ public AssetManager assetManager;
+ public Preferences prefs;
+
+ private LocalizationManager locale;
+ private PetManager petManager;
+
+ private DiscordActivityClient discordActivityClient;
+ private SessionClient sessionClient;
+
+ private static MaxonGame instance;
+
+ public static MaxonGame getInstance() {
+ if (instance == null) {
+ instance = new MaxonGame();
+ }
+ return instance;
+ }
+
+ public PetManager getPetManager() {
+ return petManager;
+ }
+
+ public DiscordActivityClient getDiscordActivityClient() {
+ return discordActivityClient;
+ }
+
+ public SessionClient getSessionClient() {
+ return sessionClient;
+ }
+
+ public LocalizationManager getLocale() {
+ return locale;
+ }
+
+ public void setLocale(LocalizationManager locale) {
+ this.locale = locale;
+ }
+
+ @Override
+ public void create() {
+ // Check the latest version
+ new GameUpdater().checkLatestUpdate();
+
+ sessionClient = new SessionClient(Gdx.app.getPreferences("kz.ilotterytea.SigninSession"));
+ batch = new SpriteBatch();
+ prefs = Gdx.app.getPreferences(MaxonConstants.GAME_APP_PACKAGE);
+ locale = new LocalizationManager(Gdx.files.internal("i18n/" + prefs.getString("lang", "en_us") + ".json"));
+
+ Gdx.graphics.setVSync(prefs.getBoolean("vsync", true));
+
+ if (prefs.getBoolean("fullscreen", false)) {
+ Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
+ } else if (
+ prefs.contains("width") ||
+ prefs.contains("height")
+ ) {
+ int width = prefs.getInteger("width", 800);
+
+ if (width < 800) {
+ width = 800;
+ prefs.putInteger("width", width);
+ }
+
+ int height = prefs.getInteger("height", 600);
+
+ if (height < 600) {
+ height = 600;
+ prefs.putInteger("height", height);
+ }
+
+ prefs.flush();
+ Gdx.graphics.setWindowedMode(width, height);
+ }
+
+ assetManager = new AssetManager();
+ petManager = new PetManager(assetManager);
+
+ discordActivityClient = new DiscordActivityClient();
+
+ this.setScreen(new SplashScreen());
+ }
+
+ @Override
+ public void dispose() {
+ batch.dispose();
+ for (String name : assetManager.getAssetNames()) {
+ assetManager.unload(name);
+ }
+ assetManager.dispose();
+ discordActivityClient.dispose();
+ }
}
diff --git a/core/src/kz/ilotterytea/maxon/localization/LineId.java b/core/src/kz/ilotterytea/maxon/localization/LineId.java
index 2ef671a..23eedc1 100644
--- a/core/src/kz/ilotterytea/maxon/localization/LineId.java
+++ b/core/src/kz/ilotterytea/maxon/localization/LineId.java
@@ -24,6 +24,15 @@ public enum LineId {
CreditsTester,
CreditsMoral,
+ LoginTitle,
+ LoginUsername,
+ LoginPassword,
+ LoginRegister,
+ LoginSend,
+ LoginProcessing,
+ LoginButton,
+ LoginAuthorised,
+
GiftboxOpen,
MinigameSlotsSpinbutton,
diff --git a/core/src/kz/ilotterytea/maxon/screens/MenuScreen.java b/core/src/kz/ilotterytea/maxon/screens/MenuScreen.java
index 43dfbca..ec60b33 100644
--- a/core/src/kz/ilotterytea/maxon/screens/MenuScreen.java
+++ b/core/src/kz/ilotterytea/maxon/screens/MenuScreen.java
@@ -1,6 +1,7 @@
package kz.ilotterytea.maxon.screens;
-import com.badlogic.gdx.*;
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.Screen;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
@@ -27,7 +28,10 @@ import kz.ilotterytea.maxon.constants.SettingsConstants;
import kz.ilotterytea.maxon.localization.LineId;
import kz.ilotterytea.maxon.localization.LocalizationManager;
import kz.ilotterytea.maxon.player.Savegame;
-import kz.ilotterytea.maxon.ui.*;
+import kz.ilotterytea.maxon.session.SessionClient;
+import kz.ilotterytea.maxon.ui.DebugWidget;
+import kz.ilotterytea.maxon.ui.SavegameWidget;
+import kz.ilotterytea.maxon.ui.ShakingImageButton;
import kz.ilotterytea.maxon.utils.GameUpdater;
import kz.ilotterytea.maxon.utils.OsUtils;
import net.mgsx.gltf.scene3d.attributes.PBRCubemapAttribute;
@@ -38,14 +42,19 @@ import net.mgsx.gltf.scene3d.scene.SceneManager;
import net.mgsx.gltf.scene3d.scene.SceneSkybox;
import net.mgsx.gltf.scene3d.utils.EnvironmentUtil;
import net.mgsx.gltf.scene3d.utils.IBLBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.ArrayList;
public class MenuScreen implements Screen {
+ private static final Logger log = LoggerFactory.getLogger(MenuScreen.class);
private MaxonGame game;
private Stage stage;
private Skin uiSkin;
+ private TextButton loginButton;
+
private Music menuMusic;
private final Savegame savegame = Savegame.getInstance();
@@ -57,7 +66,8 @@ public class MenuScreen implements Screen {
private Sound clickSound;
private float soundVolume;
- @Override public void show() {
+ @Override
+ public void show() {
this.game = MaxonGame.getInstance();
game.getDiscordActivityClient().runThread();
@@ -317,7 +327,18 @@ public class MenuScreen implements Screen {
}
});
+ // Login button
+ loginButton = new TextButton(game.getLocale().getLine(LineId.LoginButton), uiSkin);
+ loginButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ showLoginWindow();
+ }
+ });
+
if (!OsUtils.isMobile) {
+ rightGameControlTable.add(loginButton).padRight(16f);
rightGameControlTable.add(localeButton).size(iconSize).padRight(16f);
rightGameControlTable.add(musicButton).size(iconSize).padRight(16f);
rightGameControlTable.add(resolutionButton).size(iconSize);
@@ -384,6 +405,57 @@ public class MenuScreen implements Screen {
stage.act(delta);
stage.draw();
+
+ // Login button logic
+ SessionClient session = game.getSessionClient();
+ if (!session.isProcessing() && !session.isAuthorised() && !loginButton.getText().equals(game.getLocale().getLine(LineId.LoginButton))) {
+ loginButton.setText(game.getLocale().getLine(LineId.LoginButton));
+ loginButton.clearListeners();
+ loginButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ showLoginWindow();
+ }
+
+ @Override
+ public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) {
+ super.enter(event, x, y, pointer, fromActor);
+ loginButton.setColor(new Color(0xffffffff));
+ }
+
+ @Override
+ public void exit(InputEvent event, float x, float y, int pointer, Actor fromActor) {
+ super.exit(event, x, y, pointer, fromActor);
+ loginButton.setColor(new Color(0xeeeeeeff));
+ }
+ });
+ loginButton.setStyle(uiSkin.get("default", TextButton.TextButtonStyle.class));
+ loginButton.setDisabled(false);
+ } else if (session.isAuthorised() && !loginButton.getText().equals(game.getLocale().getFormattedLine(LineId.LoginAuthorised, session.getUsername()))) {
+ loginButton.setText(game.getLocale().getFormattedLine(LineId.LoginAuthorised, session.getUsername()));
+ loginButton.clearListeners();
+ loginButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ session.invalidateToken();
+ }
+
+ @Override
+ public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) {
+ super.enter(event, x, y, pointer, fromActor);
+ loginButton.setColor(new Color(0xffaaaaff));
+ }
+
+ @Override
+ public void exit(InputEvent event, float x, float y, int pointer, Actor fromActor) {
+ super.exit(event, x, y, pointer, fromActor);
+ loginButton.setColor(Color.WHITE);
+ }
+ });
+ loginButton.setDisabled(true);
+ }
}
@Override
@@ -392,9 +464,16 @@ public class MenuScreen implements Screen {
sceneManager.updateViewport(width, height);
}
- @Override public void pause() {}
- @Override public void resume() {}
- @Override public void hide() {
+ @Override
+ public void pause() {
+ }
+
+ @Override
+ public void resume() {
+ }
+
+ @Override
+ public void hide() {
for (Timer.Task task : tasks) {
task.cancel();
}
@@ -403,7 +482,9 @@ public class MenuScreen implements Screen {
menuMusic.stop();
dispose();
}
- @Override public void dispose() {
+
+ @Override
+ public void dispose() {
stage.dispose();
sceneManager.dispose();
}
@@ -669,4 +750,115 @@ public class MenuScreen implements Screen {
});
engineCredits.add(engineImage).height(OsUtils.isMobile ? 64f : 32f).width(OsUtils.isMobile ? 360f : 180f);
}
+
+ private void showLoginWindow() {
+ Image bgTint = new Image(uiSkin, "halftransparentblack");
+ bgTint.setFillParent(true);
+ stage.addActor(bgTint);
+
+ // Table
+ Table table = new Table();
+ table.setFillParent(true);
+ table.align(Align.center);
+
+ stage.addActor(table);
+
+ // Window
+ Table window = new Table(uiSkin);
+ window.setBackground("bg");
+ window.align(Align.center);
+ table.add(window).size(460f, 400f);
+
+ // Table for title and close button
+ Table titleTable = new Table(uiSkin);
+ titleTable.setBackground("bg2");
+ window.add(titleTable).growX().row();
+
+ // Title
+ Label titleLabel = new Label(game.getLocale().getLine(LineId.LoginTitle), uiSkin);
+ titleTable.add(titleLabel).pad(8f, 16f, 8f, 16f).growX();
+
+ // Close button
+ TextButton closeButton = new TextButton(" X ", uiSkin);
+ closeButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ table.remove();
+ bgTint.remove();
+ clickSound.play(soundVolume);
+ }
+ });
+ titleTable.add(closeButton).pad(8f, 16f, 8f, 16f);
+
+ // Table for fields
+ Table contentTable = new Table();
+ window.add(contentTable).pad(16f).grow().row();
+
+ // Username
+ Label usernameLabel = new Label(game.getLocale().getLine(LineId.LoginUsername), uiSkin);
+ contentTable.add(usernameLabel).grow().row();
+
+ TextField usernameField = new TextField("", uiSkin);
+ usernameField.setMessageText("...");
+ usernameField.setTextFieldFilter((textField, c) -> Character.toString(c).matches("^[a-zA-Z0-9]"));
+ usernameField.setMaxLength(25);
+ contentTable.add(usernameField).padBottom(15f).grow().row();
+
+ // Password
+ Label passwordLabel = new Label(game.getLocale().getLine(LineId.LoginPassword), uiSkin);
+ contentTable.add(passwordLabel).grow().row();
+
+ TextField passwordField = new TextField("", uiSkin);
+ passwordField.setMessageText("...");
+
+ String[] passwords = {"", ""};
+
+ passwordField.setTextFieldListener((textField, c) -> {
+ String hiddenText = passwords[0];
+ String fieldText = passwords[1];
+
+ if (c == 8 && !fieldText.isEmpty() && !hiddenText.isEmpty()) {
+ fieldText = fieldText.substring(0, fieldText.length() - 1);
+ hiddenText = hiddenText.substring(0, hiddenText.length() - 1);
+ } else if (c != 8) {
+ fieldText += '*';
+ hiddenText += c;
+ }
+
+ textField.setText(fieldText);
+ passwords[0] = hiddenText;
+ passwords[1] = fieldText;
+ });
+
+ contentTable.add(passwordField).grow().row();
+
+ // Register button
+ TextButton registerButton = new TextButton(game.getLocale().getLine(LineId.LoginRegister), uiSkin, "link");
+ registerButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ Gdx.net.openURI(MaxonConstants.IDENTITY_REGISTRATION_URL);
+ }
+ });
+ contentTable.add(registerButton).padTop(15f).padBottom(15f).grow().row();
+
+ // Login button
+ TextButton sendButton = new TextButton(game.getLocale().getLine(LineId.LoginSend), uiSkin);
+ sendButton.addListener(new ClickListener() {
+ @Override
+ public void clicked(InputEvent event, float x, float y) {
+ super.clicked(event, x, y);
+ MaxonGame.getInstance().getSessionClient().authorize(usernameField.getText(), passwords[0]);
+ loginButton.setText(game.getLocale().getLine(LineId.LoginProcessing));
+ loginButton.setDisabled(true);
+ // maybe we could somehow fire the close button instead of cv pasting
+ table.remove();
+ bgTint.remove();
+ clickSound.play(soundVolume);
+ }
+ });
+ contentTable.add(sendButton).grow().row();
+ }
}
diff --git a/core/src/kz/ilotterytea/maxon/session/SessionClient.java b/core/src/kz/ilotterytea/maxon/session/SessionClient.java
new file mode 100644
index 0000000..31edd6e
--- /dev/null
+++ b/core/src/kz/ilotterytea/maxon/session/SessionClient.java
@@ -0,0 +1,323 @@
+package kz.ilotterytea.maxon.session;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.Net;
+import com.badlogic.gdx.Preferences;
+import com.badlogic.gdx.net.HttpRequestBuilder;
+import com.badlogic.gdx.net.HttpStatus;
+import com.badlogic.gdx.utils.JsonReader;
+import com.badlogic.gdx.utils.JsonValue;
+import com.badlogic.gdx.utils.JsonWriter;
+import com.badlogic.gdx.utils.Timer;
+import kz.ilotterytea.maxon.MaxonConstants;
+import kz.ilotterytea.maxon.utils.RandomUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SessionClient {
+ private final Logger log;
+
+ private final Preferences sessionPreferences;
+ private final String clientToken;
+ private String accessToken;
+ private String userId, username, password;
+ private boolean isProcessing, isAuthorised;
+
+ public SessionClient(Preferences sessionPreferences) {
+ startValidationThread();
+ this.log = LoggerFactory.getLogger(SessionClient.class);
+
+ this.clientToken = RandomUtils.generateRandomString();
+ this.sessionPreferences = sessionPreferences;
+ this.isProcessing = false;
+ this.isAuthorised = false;
+
+ if (sessionPreferences.contains("username") && sessionPreferences.contains("password")) {
+ this.authorize(sessionPreferences.getString("username"), sessionPreferences.getString("password"));
+ }
+ }
+
+ public void authorize(String username, String password) {
+ log.info("Authorising...");
+ this.isProcessing = true;
+ sessionPreferences.putString("username", username);
+ sessionPreferences.putString("password", password);
+ sessionPreferences.flush();
+
+ JsonValue agent = new JsonValue(JsonValue.ValueType.object);
+ agent.addChild("name", new JsonValue(String.valueOf(MaxonConstants.GAME_APP_ID.charAt(0)).toUpperCase() + MaxonConstants.GAME_APP_ID.substring(1)));
+ agent.addChild("version", new JsonValue(MaxonConstants.GAME_PROTOCOL));
+
+ JsonValue json = new JsonValue(JsonValue.ValueType.object);
+ json.addChild("agent", agent);
+ json.addChild("username", new JsonValue(username));
+ json.addChild("password", new JsonValue(password));
+ json.addChild("clientToken", new JsonValue(clientToken));
+
+ Net.HttpRequest request =
+ new HttpRequestBuilder()
+ .newRequest()
+ .method(Net.HttpMethods.POST)
+ .url(MaxonConstants.IDENTITY_AUTHENTICATION_URL)
+ .timeout(20000)
+ .header("Content-Type", "application/json")
+ .content(json.toJson(JsonWriter.OutputType.json))
+ .build();
+
+ Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {
+ @Override
+ public void handleHttpResponse(Net.HttpResponse httpResponse) {
+ SessionClient.this.isProcessing = false;
+
+ try {
+ JsonValue json = new JsonReader().parse(httpResponse.getResultAsString());
+
+ if (httpResponse.getStatus().getStatusCode() != HttpStatus.SC_OK) {
+ String type = json.get("error").getString("type");
+ String error = json.get("error").getString("message");
+ log.error("Failed to authorise: {} ({})", error, type);
+
+ sessionPreferences.remove("username");
+ sessionPreferences.remove("password");
+ sessionPreferences.flush();
+
+ return;
+ }
+
+ SessionClient.this.username = username;
+ SessionClient.this.password = password;
+ SessionClient.this.accessToken = json.get("data").getString("accessToken");
+ SessionClient.this.userId = String.valueOf(json.get("data").get("user").getInt("id"));
+ SessionClient.this.isAuthorised = true;
+ log.info("Successfully authorised! Welcome back, {}!", SessionClient.this.username);
+ } catch (Exception e) {
+ log.error("An exception was thrown while authorising", e);
+ }
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ log.error("Failed to send an authorisation request", t);
+ }
+
+ @Override
+ public void cancelled() {
+ log.info("Authorisation request was cancelled!");
+ }
+ });
+ }
+
+ public void validateToken() {
+ if (clientToken == null || accessToken == null) {
+ return;
+ }
+
+ log.info("Validating token...");
+
+ JsonValue json = new JsonValue(JsonValue.ValueType.object);
+ json.addChild("clientToken", new JsonValue(clientToken));
+ json.addChild("accessToken", new JsonValue(accessToken));
+
+ Net.HttpRequest request =
+ new HttpRequestBuilder()
+ .newRequest()
+ .method(Net.HttpMethods.POST)
+ .url(MaxonConstants.IDENTITY_VALIDATE_URL)
+ .timeout(20000)
+ .header("Content-Type", "application/json")
+ .content(json.toJson(JsonWriter.OutputType.json))
+ .build();
+
+ Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {
+ @Override
+ public void handleHttpResponse(Net.HttpResponse httpResponse) {
+ try {
+ JsonValue json = new JsonReader().parse(httpResponse.getResultAsString());
+
+ if (httpResponse.getStatus().getStatusCode() != HttpStatus.SC_OK) {
+ String type = json.get("error").getString("type");
+ String error = json.get("error").getString("message");
+ log.error("Failed to validate: {} ({})", error, type);
+ accessToken = null;
+ userId = null;
+ isAuthorised = false;
+ authorize(username, password);
+ return;
+ }
+
+ int expiresInSeconds = json.get("data").getInt("expiresInSeconds");
+
+ if (expiresInSeconds < 1000) {
+ refreshToken();
+ }
+
+ log.info("Token validated!");
+ } catch (Exception e) {
+ log.error("An exception was thrown while validating", e);
+ }
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ log.error("Failed to send a validation request", t);
+ }
+
+ @Override
+ public void cancelled() {
+ log.info("Validation request was cancelled!");
+ }
+ });
+ }
+
+ public void invalidateToken() {
+ if (clientToken == null || accessToken == null) {
+ return;
+ }
+
+ log.info("Invalidating token...");
+
+ JsonValue json = new JsonValue(JsonValue.ValueType.object);
+ json.addChild("clientToken", new JsonValue(clientToken));
+ json.addChild("accessToken", new JsonValue(accessToken));
+
+ Net.HttpRequest request =
+ new HttpRequestBuilder()
+ .newRequest()
+ .method(Net.HttpMethods.POST)
+ .url(MaxonConstants.IDENTITY_INVALIDATE_URL)
+ .timeout(20000)
+ .header("Content-Type", "application/json")
+ .content(json.toJson(JsonWriter.OutputType.json))
+ .build();
+
+ Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {
+ @Override
+ public void handleHttpResponse(Net.HttpResponse httpResponse) {
+ try {
+ JsonValue json = new JsonReader().parse(httpResponse.getResultAsString());
+
+ if (httpResponse.getStatus().getStatusCode() != HttpStatus.SC_OK) {
+ String type = json.get("error").getString("type");
+ String error = json.get("error").getString("message");
+ log.error("Failed to invalidate: {} ({})", error, type);
+ return;
+ }
+
+ log.info("Invalidated! Bye, {}", username);
+
+ accessToken = null;
+ userId = null;
+ username = null;
+ password = null;
+ isAuthorised = false;
+ sessionPreferences.remove("username");
+ sessionPreferences.remove("password");
+ sessionPreferences.flush();
+ } catch (Exception ignored) {
+ }
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ }
+
+ @Override
+ public void cancelled() {
+ }
+ });
+ }
+
+ public void refreshToken() {
+ if (clientToken == null || accessToken == null) {
+ return;
+ }
+
+ log.info("Refreshing token...");
+
+ JsonValue json = new JsonValue(JsonValue.ValueType.object);
+ json.addChild("clientToken", new JsonValue(clientToken));
+ json.addChild("accessToken", new JsonValue(accessToken));
+
+ Net.HttpRequest request =
+ new HttpRequestBuilder()
+ .newRequest()
+ .method(Net.HttpMethods.POST)
+ .url(MaxonConstants.IDENTITY_REFRESH_URL)
+ .timeout(20000)
+ .header("Content-Type", "application/json")
+ .content(json.toJson(JsonWriter.OutputType.json))
+ .build();
+
+ Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {
+ @Override
+ public void handleHttpResponse(Net.HttpResponse httpResponse) {
+ try {
+ JsonValue json = new JsonReader().parse(httpResponse.getResultAsString());
+
+ if (httpResponse.getStatus().getStatusCode() != HttpStatus.SC_OK) {
+ String type = json.get("error").getString("type");
+ String error = json.get("error").getString("message");
+ log.error("Failed to refresh: {} ({})", error, type);
+ accessToken = null;
+ userId = null;
+ isAuthorised = false;
+ log.warn(error);
+ return;
+ }
+
+ accessToken = json.get("data").get("accessToken").asString();
+ log.info("Token has been refreshed!");
+ } catch (Exception e) {
+ log.error("An exception was thrown while refreshing", e);
+ }
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ log.error("Failed to send a refresh request", t);
+ }
+
+ @Override
+ public void cancelled() {
+ log.info("Refresh request was cancelled!");
+ }
+ });
+ }
+
+ public boolean isAuthorised() {
+ return this.isAuthorised;
+ }
+
+ public boolean isProcessing() {
+ return isProcessing;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public String getClientToken() {
+ return clientToken;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ private void startValidationThread() {
+ Timer.schedule(new Timer.Task() {
+ @Override
+ public void run() {
+ validateToken();
+ }
+ }, 60000, 60000);
+ }
+}