diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index a61caf6d22..04d7b31f8b 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -15,7 +15,9 @@ UnusedAttribute: Since we're targeting a range of API levels, it's OK that e.g. appCategory will be ignored by OLD devices IconDensities: See https://developer.android.com/training/tv/start/start#banner for the banner attribute, where they recommend supplying only one density. --> + preview2.turns } + companion object { + fun usesCustomServer() = UncivGame.Current.settings.multiplayer.server != Constants.dropboxMultiplayerServer + fun usesDropbox() = !usesCustomServer() + } + } /** diff --git a/core/src/com/unciv/logic/multiplayer/OnlineMultiplayerGame.kt b/core/src/com/unciv/logic/multiplayer/OnlineMultiplayerGame.kt index 48fd191f7f..fd8532a447 100644 --- a/core/src/com/unciv/logic/multiplayer/OnlineMultiplayerGame.kt +++ b/core/src/com/unciv/logic/multiplayer/OnlineMultiplayerGame.kt @@ -116,6 +116,5 @@ private enum class GameUpdateResult { * How often games can be checked for remote updates. More attempted checks within this time period will do nothing. */ private fun getUpdateThrottleInterval(): Duration { - val isDropbox = UncivGame.Current.settings.multiplayer.server == Constants.dropboxMultiplayerServer - return Duration.ofSeconds(if (isDropbox) DROPBOX_THROTTLE_PERIOD else CUSTOM_SERVER_THROTTLE_PERIOD) + return Duration.ofSeconds(if (OnlineMultiplayer.usesCustomServer()) CUSTOM_SERVER_THROTTLE_PERIOD else DROPBOX_THROTTLE_PERIOD) } diff --git a/core/src/com/unciv/models/metadata/GameSettings.kt b/core/src/com/unciv/models/metadata/GameSettings.kt index 4c5a783c6d..25a8acce1c 100644 --- a/core/src/com/unciv/models/metadata/GameSettings.kt +++ b/core/src/com/unciv/models/metadata/GameSettings.kt @@ -165,4 +165,5 @@ class GameSettingsMultiplayer { var statusButtonInSinglePlayer = false var currentGameRefreshDelay = Duration.ofSeconds(10) var allGameRefreshDelay = Duration.ofMinutes(5) + var hideDropboxWarning = false } diff --git a/core/src/com/unciv/ui/multiplayer/MultiplayerHelpers.kt b/core/src/com/unciv/ui/multiplayer/MultiplayerHelpers.kt index 80dec08f9a..24e07d8dba 100644 --- a/core/src/com/unciv/ui/multiplayer/MultiplayerHelpers.kt +++ b/core/src/com/unciv/ui/multiplayer/MultiplayerHelpers.kt @@ -1,7 +1,9 @@ package com.unciv.ui.multiplayer +import com.badlogic.gdx.Gdx import com.unciv.UncivGame import com.unciv.logic.UncivShowableException +import com.unciv.logic.multiplayer.OnlineMultiplayer import com.unciv.logic.multiplayer.OnlineMultiplayerGame import com.unciv.logic.multiplayer.storage.FileStorageRateLimitReached import com.unciv.models.translations.tr @@ -9,6 +11,7 @@ import com.unciv.ui.crashhandling.launchCrashHandling import com.unciv.ui.crashhandling.postCrashHandlingRunnable import com.unciv.ui.popup.Popup import com.unciv.ui.utils.BaseScreen +import com.unciv.ui.utils.toCheckBox import java.io.FileNotFoundException import java.time.Duration import java.time.Instant @@ -67,4 +70,25 @@ object MultiplayerHelpers { return "[${durationToNow.toDays()}] [Days]" } } + + fun showDropboxWarning(screen: BaseScreen) { + if (!OnlineMultiplayer.usesDropbox() || UncivGame.Current.settings.multiplayer.hideDropboxWarning) return + + val dropboxWarning = Popup(screen) + dropboxWarning.addGoodSizedLabel( + "You're currently using the default multiplayer server, which is based on a free Dropbox account. " + + "Because a lot of people use this, it is uncertain if you'll actually be able to access it consistently. " + + "Consider using a custom server instead." + ).colspan(2).row() + dropboxWarning.addButton("Open Documentation") { + Gdx.net.openURI("https://yairm210.github.io/Unciv/Other/Hosting-a-Multiplayer-server/") + }.colspan(2).row() + + val checkBox = "Don't show again".toCheckBox() + dropboxWarning.add(checkBox) + dropboxWarning.addCloseButton { + UncivGame.Current.settings.multiplayer.hideDropboxWarning = checkBox.isChecked + } + dropboxWarning.open() + } } diff --git a/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt b/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt index 20c5e069c2..a17654e13c 100644 --- a/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt @@ -1,8 +1,12 @@ package com.unciv.ui.newgamescreen +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.scenes.scene2d.ui.CheckBox +import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.scenes.scene2d.ui.Table import com.unciv.UncivGame import com.unciv.logic.civilization.CityStateType +import com.unciv.logic.multiplayer.OnlineMultiplayer import com.unciv.models.metadata.GameSpeed import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.unique.UniqueType @@ -10,6 +14,8 @@ import com.unciv.models.translations.tr import com.unciv.ui.audio.MusicMood import com.unciv.ui.audio.MusicTrackChooserFlags import com.unciv.ui.images.ImageGetter +import com.unciv.ui.multiplayer.MultiplayerHelpers +import com.unciv.ui.popup.Popup import com.unciv.ui.popup.ToastPopup import com.unciv.ui.utils.* @@ -100,9 +106,12 @@ class GameOptionsTable( private fun Table.addIsOnlineMultiplayerCheckbox() = addCheckbox("Online Multiplayer", gameParameters.isOnlineMultiplayer) - { - gameParameters.isOnlineMultiplayer = it + { shouldUseMultiplayer -> + gameParameters.isOnlineMultiplayer = shouldUseMultiplayer updatePlayerPickerTable("") + if (shouldUseMultiplayer) { + MultiplayerHelpers.showDropboxWarning(previousScreen as BaseScreen) + } } private fun numberOfCityStates() = ruleset.nations.values.count { diff --git a/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt b/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt index b9de20b7b1..910c216b56 100644 --- a/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt +++ b/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt @@ -73,10 +73,9 @@ class NewGameScreen( rightSideButton.setText("Start game!".tr()) rightSideButton.onClick { if (gameSetupInfo.gameParameters.isOnlineMultiplayer) { - val isDropbox = UncivGame.Current.settings.multiplayer.server == Constants.dropboxMultiplayerServer if (!checkConnectionToMultiplayerServer()) { val noInternetConnectionPopup = Popup(this) - val label = if (isDropbox) "Couldn't connect to Dropbox!" else "Couldn't connect to Multiplayer Server!" + val label = if (OnlineMultiplayer.usesCustomServer()) "Couldn't connect to Multiplayer Server!" else "Couldn't connect to Dropbox!" noInternetConnectionPopup.addGoodSizedLabel(label.tr()).row() noInternetConnectionPopup.addCloseButton() noInternetConnectionPopup.open() diff --git a/core/src/com/unciv/ui/options/MultiplayerTab.kt b/core/src/com/unciv/ui/options/MultiplayerTab.kt index 0f96ab6cdd..300fd2b494 100644 --- a/core/src/com/unciv/ui/options/MultiplayerTab.kt +++ b/core/src/com/unciv/ui/options/MultiplayerTab.kt @@ -8,6 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.TextField import com.badlogic.gdx.utils.Array import com.unciv.Constants +import com.unciv.logic.multiplayer.OnlineMultiplayer import com.unciv.logic.multiplayer.storage.SimpleHttp import com.unciv.models.metadata.GameSettings import com.unciv.models.translations.tr @@ -64,7 +65,7 @@ fun multiplayerTab( val connectionToServerButton = "Check connection to server".toTextButton() val textToShowForMultiplayerAddress = - if (!usesDropbox(settings)) settings.multiplayer.server + if (OnlineMultiplayer.usesCustomServer()) settings.multiplayer.server else "https://..." val multiplayerServerTextField = TextField(textToShowForMultiplayerAddress, BaseScreen.skin) multiplayerServerTextField.setTextFieldFilter { _, c -> c !in " \r\n\t\\" } @@ -203,8 +204,6 @@ private fun List.toGdxArray(): Array { return arr } -private fun usesDropbox(settings: GameSettings) = settings.multiplayer.server == Constants.dropboxMultiplayerServer - private fun addRefreshSelect( table: Table, settings: GameSettings, @@ -216,10 +215,10 @@ private fun addRefreshSelect( table.add(label).left() val refreshSelectBox = SelectBox(table.skin) - val options = if (usesDropbox(settings)) { - dropboxOptions - } else { + val options = if (OnlineMultiplayer.usesCustomServer()) { customServerOptions + } else { + dropboxOptions } refreshSelectBox.items = options diff --git a/docs/Other/Hosting-a-Multiplayer-server.md b/docs/Other/Hosting-a-Multiplayer-server.md index 390cc3dd6d..28f54c463b 100644 --- a/docs/Other/Hosting-a-Multiplayer-server.md +++ b/docs/Other/Hosting-a-Multiplayer-server.md @@ -2,35 +2,37 @@ Due to certain limitations on Dropbox's API, with the current influx of players, we've many times reached the point that Dropbox has become unavailable. -Therefore, you can now host your own Unciv server, when not on Android. +Therefore, you can now host your own Unciv server on any computer that can run Java programs. -To do so, you must have a JDK installed. +This guide is written for people with a moderate amount of technical knowledge about computer software and who are able to search the web to learn stuff they might not know. If you're completely new to this, you'll likely not be able to follow without some larger time investment to learn. -From the directory where the UncivServer.jar file is located, create a folder named "MultiplayerFiles", open a terminal and run the following line: +If you're proficient in server hosting, there's another how-to for you at the end. + +## How To + +Before starting, you must have a Java JDK installed. You'll also have to download the [latest UncivServer.jar](https://github.com/yairm210/Unciv/releases/latest/download/UncivServer.jar). + +From the directory where the `UncivServer.jar` file is located, create a folder named "MultiplayerFiles", open a terminal (in Windows, Shift+RightClick in the folder) and run the following command in the directory: `java -jar UncivServer.jar` -Don't forget to use 'cd' to switch to the correct dictionary. Here's an example in Windows. - -``` -D: -cd Games -cd unciv -mkdir MultiplayerFiles -java -jar UncivServer.jar -``` - Your server has now started! -In Unciv itself, from the same computer, enter Options > Multiplayer. +To check if everything works, you can start Unciv on the same computer, go to "Options > Multiplayer", then enter `http://localhost` as the "Server address" and click "Check connection to server". You should now get a "Success!" result, which means it's working! -Enter the URL of the computer you ran the server on (or http://localhost) +To connect with other devices, you'll need the port (default 80) the server is running on to be visible externally (port forwarding) and know your external IP-address. -If you click "check connection to server" you should now get "Return result: true", which means it's working! +On the other device, enter the URL to your server, click 'check connection' from the new device, and if you get the same "Success!" result - congratulations, you're connected to the same server and can start a multiplayer game! -For other devices, you'll need an external IP, which is out of scope for this documentation since there are many ways of achieving it. +Please note: +* Devices *not* connected to the same server will *not* be able to participate in multiplayer games together +* In many places, your external IP address changes periodically. If that is the case, you either have to update the IP all the time or use something like a dynamic DNS service. -On the other device, do the same - enter the URL, click 'check connection' from the new device, and if you got the same result - congratulations, you're both connected to the same server and can start a multiplayer game on the server! +## How To for people with hosting experience -Android has some restrictions and does not allow unencrypted HTTP traffic from the Unciv app to a server. So you need to have a reverse proxy that sits between your (Android) client(s) and the Unciv server. The reverse proxy then needs to have a valid certificate and handles the TLS sessions for your Unciv server. - -Please note that devices NOT connected to the same server will NOT be able to participate in multiplayer games together! +* Have a Java JDK installed +* Download the [latest UncivServer.jar](https://github.com/yairm210/Unciv/releases/latest/download/UncivServer.jar) (can also use that link to automatically update probably) +* See options with `java -jar UncivServer.jar --help` + * The server will run on a specified port (`-p`, default `80`), writing files in a folder (`-f`, default `./MultiplayerGames/`), so it needs appropriate permissions. +* Run it: `java -jar UncivServer.jar -p 8080 -f /some/folder/` + * It basically just does simple file storage over HTTP. + * Files are not cleaned automatically if a game ends or is deleted on the client-side