/*
* Copyright 2015 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.rendering.nui.layers.mainMenu;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.config.NetworkConfig;
import org.terasology.config.ServerInfo;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
/**
* Downloads a list of servers from a given URL.
*/
class ServerListDownloader {
private static final Logger logger = LoggerFactory.getLogger(ServerListDownloader.class);
private static final Gson GSON = new GsonBuilder().create();
private final List<ServerInfo> servers = new CopyOnWriteArrayList<>();
private final Charset charset = StandardCharsets.UTF_8;
private final String serverAddress;
/**
* The i18n key corresponding to the current status of the downloader
*
* "volatile" ensures the visibility of updates across different threads
*/
private volatile String status;
private final Thread dlThread;
ServerListDownloader(String serverAddress) {
this.serverAddress = serverAddress;
dlThread = new Thread(this::download);
dlThread.setName("ServerList Downloader");
dlThread.start();
}
/**
* @return a <b>thread-safe</b> list of servers
*/
public List<ServerInfo> getServers() {
return Collections.unmodifiableList(servers);
}
public boolean isDone() {
return !dlThread.isAlive();
}
/**
* @return the i18n key corresponding to the current status of the downloader
*/
public String getStatus() {
return status;
}
// this is run on a parallel thread
private void download() {
try {
try {
download(serverAddress);
} catch (Exception e) {
String defaultAddress = new NetworkConfig().getMasterServer();
if (!defaultAddress.equals(serverAddress)) {
logger.warn("Download server list from {} failed. Trying default ..", serverAddress);
download(defaultAddress);
} else {
throw e;
}
}
} catch (Exception e) {
status = "${engine:menu#error-downloading-server-list}";
// we catch Exception here to make sure that it's being logged
// alternative: re-throw as RuntimeException and use
// Thread.setUncaughtExceptionHandler()
logger.error("Error downloading online server list!", e);
}
}
private void download(String address) throws IOException {
status = "${engine:menu#downloading-server-list}";
URL url = new URL("http", address, "/servers/list");
try (Reader reader = new InputStreamReader(url.openStream(), charset);
JsonReader jsonReader = new JsonReader(reader)) {
status = "${engine:menu#parsing-content}";
jsonReader.beginArray();
TypeAdapter<ServerInfo> adapter = GSON.getAdapter(ServerInfo.class);
while (jsonReader.hasNext()) {
ServerInfo entry = adapter.read(jsonReader);
servers.add(entry);
logger.info("Retrieved game server {}", entry);
try {
Thread.sleep(250);
} catch (InterruptedException e) {
// ignore - this is just to create an animation anyway
}
}
jsonReader.endArray();
status = String.format("${engine:menu#server-list-complete}");
}
}
}