Complete refactoring of workshop / Schematic+mod support

This commit is contained in:
Anuken
2019-10-19 18:53:27 -04:00
parent d0dc21a52c
commit 2586c53f0d
16 changed files with 441 additions and 216 deletions

View File

@@ -22,10 +22,10 @@ import io.anuke.mindustry.core.GameState.*;
import io.anuke.mindustry.desktop.steam.*;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.mod.Mods.*;
import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.*;
import java.io.*;
@@ -249,34 +249,18 @@ public class DesktopLauncher extends ClientLauncher{
}
@Override
public Array<FileHandle> getExternalMaps(){
return !steam ? super.getExternalMaps() : SVars.workshop.getMapFiles();
public Array<FileHandle> getWorkshopContent(Class<? extends Publishable> type){
return !steam ? super.getWorkshopContent(type) : SVars.workshop.getWorkshopFiles(type);
}
@Override
public Array<FileHandle> getExternalMods(){
return !steam ? super.getExternalMods() : SVars.workshop.getModFiles();
public void viewListing(Publishable pub){
SVars.workshop.viewListing(pub);
}
@Override
public Array<FileHandle> getExternalSchematics(){
return !steam ? super.getExternalMods() : SVars.workshop.getSchematicFiles();
}
@Override
public void viewMapListing(Map map){
viewListing(map.file.parent().name());
}
@Override
public void viewListing(String mapid){
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + mapid);
}
@Override
public void viewMapListingInfo(Map map){
SVars.workshop.viewMapListingInfo(map);
public void viewListingID(String id){
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + id);
}
@Override
@@ -290,8 +274,8 @@ public class DesktopLauncher extends ClientLauncher{
}
@Override
public void publishMap(Map map){
SVars.workshop.publishMap(map);
public void publish(Publishable pub){
SVars.workshop.publish(pub);
}
@Override

View File

@@ -9,11 +9,11 @@ import io.anuke.arc.files.*;
import io.anuke.arc.function.*;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.util.*;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.maps.*;
import io.anuke.mindustry.mod.Mods.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.dialogs.*;
import static io.anuke.mindustry.Vars.*;
@@ -21,12 +21,10 @@ import static io.anuke.mindustry.Vars.*;
public class SWorkshop implements SteamUGCCallback{
public final SteamUGC ugc = new SteamUGC(this);
//private Map lastMap;
private Array<FileHandle> mapFiles;
private Array<FileHandle> modFiles;
private Array<FileHandle> schematicFiles;
private ObjectMap<Class<? extends Publishable>, Array<FileHandle>> workshopFiles = new ObjectMap<>();
private ObjectMap<SteamUGCQuery, BiConsumer<Array<SteamUGCDetails>, SteamResult>> detailHandlers = new ObjectMap<>();
private Array<Consumer<SteamPublishedFileID>> itemHandlers = new Array<>();
private ObjectMap<SteamPublishedFileID, Runnable> updatedHandlers = new ObjectMap<>();
public SWorkshop(){
int items = ugc.getNumSubscribedItems();
@@ -39,88 +37,56 @@ public class SWorkshop implements SteamUGCCallback{
return new FileHandle(info.getFolder());
}).select(f -> f != null && f.list().length > 0);
mapFiles = folders.select(f -> f.list().length == 1 && f.list()[0].extension().equals(mapExtension)).map(f -> f.list()[0]);
schematicFiles = folders.select(f -> f.list().length == 1 && f.list()[0].extension().equals(schematicExtension)).map(f -> f.list()[0]);
modFiles = folders.select(f -> f.child("mod.json").exists());
workshopFiles.put(Map.class, folders.select(f -> f.list().length == 1 && f.list()[0].extension().equals(mapExtension)).map(f -> f.list()[0]));
workshopFiles.put(Schematic.class, folders.select(f -> f.list().length == 1 && f.list()[0].extension().equals(schematicExtension)).map(f -> f.list()[0]));
workshopFiles.put(LoadedMod.class, folders.select(f -> f.child("mod.json").exists()));
if(!mapFiles.isEmpty()){
if(!workshopFiles.get(Map.class).isEmpty()){
SAchievement.downloadMapWorkshop.complete();
}
Log.info("Fetching {0} subscribed maps.", mapFiles.size);
Log.info("Fetching {0} subscribed mods.", modFiles.size);
}
public Array<FileHandle> getMapFiles(){
return mapFiles;
}
public Array<FileHandle> getModFiles(){
return modFiles;
}
public Array<FileHandle> getSchematicFiles(){
return schematicFiles;
}
public void publishMap(Map map){
if(map.tags.containsKey("steamid")){
Log.info("Map already published, redirecting to ID.");
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + map.tags.get("steamid"));
return;
}
//update author name when publishing
map.tags.put("author", SVars.net.friends.getPersonaName());
ui.editor.editor.getTags().put("author", map.tags.get("author"));
ui.editor.save();
showPublish(id -> updateMap(map, id, "<Map Created>"));
}
public void publishSchematic(Schematic schematic){
showPublish(id -> {
workshopFiles.each((type, list) -> {
Log.info("Fetched content ({0}): {1}", type.getSimpleName(), list.size);
});
}
public void publishMod(LoadedMod mod){
public Array<FileHandle> getWorkshopFiles(Class<? extends Publishable> type){
return workshopFiles.getOr(type, () -> new Array<>(0));
}
private void showPublish(Consumer<SteamPublishedFileID> published){
FloatingDialog dialog = new FloatingDialog("$confirm");
dialog.setFillParent(false);
dialog.cont.add("$publish.confirm").width(600f).wrap();
dialog.addCloseButton();
dialog.buttons.addImageTextButton("$eula", Icon.linkSmall, () -> {
SVars.net.friends.activateGameOverlayToWebPage("https://steamcommunity.com/sharedfiles/workshoplegalagreement");
}).size(210f, 64f);
/** Publish a new item and submit an update for it.
* If it is already published, redirects to its page.*/
public void publish(Publishable p){
if(p.hasSteamID()){
Log.info("Content already published, redirecting to ID.");
viewListing(p);
return;
}
dialog.buttons.addImageTextButton("$ok", Icon.checkSmall, () -> {
ugc.createItem(SVars.steamID, WorkshopFileType.Community);
ui.loadfrag.show("$publishing");
dialog.hide();
itemHandlers.add(published);
}).size(170f, 64f);
dialog.show();
if(!p.prePublish()){
return;
}
showPublish(id -> update(p, id, null));
}
public void viewMapListingInfo(Map map){
String id = map.tags.get("steamid");
/** Update an existing item with a changelog. */
public void updateItem(Publishable p, String changelog){
String id = p.getSteamID();
long handle = Strings.parseLong(id, -1);
SteamPublishedFileID fid = new SteamPublishedFileID(handle);
update(p, fid, changelog);
}
Log.info("Requesting map listing view; id = " + id);
/** Fetches info for an item, checking to make sure that it exists.*/
public void viewListing(Publishable p){
long handle = Strings.parseLong(p.getSteamID(), -1);
SteamPublishedFileID id = new SteamPublishedFileID(handle);
ui.loadfrag.show();
query(ugc.createQueryUGCDetailsRequest(fid), (detailsList, result) -> {
query(ugc.createQueryUGCDetailsRequest(id), (detailsList, result) -> {
ui.loadfrag.hide();
Log.info("Map listing result: " + result + " " + detailsList);
if(result == SteamResult.OK){
SteamUGCDetails details = detailsList.first();
if(details.getResult() == SteamResult.OK){
@@ -132,57 +98,114 @@ public class SWorkshop implements SteamUGCCallback{
dialog.addCloseButton();
dialog.buttons.addImageTextButton("$view.workshop", Icon.linkSmall, () -> {
platform.viewListing(id);
viewListingID(id);
dialog.hide();
}).size(210f, 64f);
dialog.buttons.addImageTextButton("$map.update", Icon.upgradeSmall, () -> {
new FloatingDialog("$map.update"){{
dialog.buttons.addImageTextButton("$workshop.update", Icon.upgradeSmall, () -> {
new FloatingDialog("$workshop.update"){{
setFillParent(false);
cont.margin(10).add("$map.changelog").padRight(6f);
cont.margin(10).add("$changelog").padRight(6f);
cont.row();
TextArea field = cont.addArea("", t -> {}).size(500f, 160f).get();
field.setMaxLength(400);
buttons.defaults().size(120, 54).pad(4);
buttons.addButton("$ok", () -> {
ui.loadfrag.show("publishing");
updateMap(map, details.getPublishedFileID(), field.getText().replace("\r", "\n"));
ui.loadfrag.show("$publishing");
updateItem(p, field.getText().replace("\r", "\n"));
dialog.hide();
hide();
Log.info("Update map " + map.name());
});
buttons.addButton("$cancel", this::hide);
}}.show();
}).size(210f, 64f);
dialog.show();
}else{
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + SteamNativeHandle.getNativeHandle(details.getPublishedFileID()));
}
}else if(details.getResult() == SteamResult.FileNotFound){
//force-remove tags
ui.editor.editor.getTags().remove("steamid");
map.tags.remove("steamid");
ui.editor.save();
ui.showErrorMessage("$map.missing");
p.removeSteamID();
ui.showErrorMessage("$missing");
}else{
ui.showErrorMessage(Core.bundle.format("map.load.error", result.name()));
ui.showErrorMessage(Core.bundle.format("workshop.error", result.name()));
}
}else{
ui.showErrorMessage(Core.bundle.format("map.load.error", result.name()));
ui.showErrorMessage(Core.bundle.format("workshop.error", result.name()));
}
});
}
public void query(SteamUGCQuery query, BiConsumer<Array<SteamUGCDetails>, SteamResult> handler){
Log.info("POST " + query);
void viewListingID(SteamPublishedFileID id){
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + SteamNativeHandle.getNativeHandle(id));
}
void update(Publishable p, SteamPublishedFileID id, String changelog){
String sid = SteamNativeHandle.getNativeHandle(id) + "";
updateItem(id, h -> {
if(p.steamDescription() != null){
ugc.setItemDescription(h, p.steamDescription());
}
Array<String> tags = p.extraTags();
tags.add(p.steamTag());
ugc.setItemTitle(h, p.steamTitle());
ugc.setItemTags(h, tags.toArray(String.class));
ugc.setItemPreview(h, p.createSteamPreview(sid).absolutePath());
ugc.setItemContent(h, p.createSteamFolder(sid).absolutePath());
if(changelog == null){
ugc.setItemVisibility(h, PublishedFileVisibility.Private);
}
ugc.submitItemUpdate(h, changelog == null ? "<Created>" : changelog);
}, () -> p.addSteamID(sid));
}
void showPublish(Consumer<SteamPublishedFileID> published){
FloatingDialog dialog = new FloatingDialog("$confirm");
dialog.setFillParent(false);
dialog.cont.add("$publish.confirm").width(600f).wrap();
dialog.addCloseButton();
dialog.buttons.addImageTextButton("$eula", Icon.linkSmall,
() -> SVars.net.friends.activateGameOverlayToWebPage("https://steamcommunity.com/sharedfiles/workshoplegalagreement"))
.size(210f, 64f);
dialog.buttons.addImageTextButton("$ok", Icon.checkSmall, () -> {
ugc.createItem(SVars.steamID, WorkshopFileType.Community);
ui.loadfrag.show("$publishing");
dialog.hide();
itemHandlers.add(published);
}).size(170f, 64f);
dialog.show();
}
void query(SteamUGCQuery query, BiConsumer<Array<SteamUGCDetails>, SteamResult> handler){
Log.info("POST QUERY " + query);
detailHandlers.put(query, handler);
ugc.sendQueryUGCRequest(query);
}
void updateItem(SteamPublishedFileID publishedFileID, Consumer<SteamUGCUpdateHandle> tagger, Runnable updated){
SteamUGCUpdateHandle h = ugc.startItemUpdate(SVars.steamID, publishedFileID);
tagger.accept(h);
ItemUpdateInfo info = new ItemUpdateInfo();
ui.loadfrag.setProgress(() -> {
ItemUpdateStatus status = ugc.getItemUpdateProgress(h, info);
ui.loadfrag.setText("$" + status.name().toLowerCase());
if(status == ItemUpdateStatus.Invalid){
ui.loadfrag.setText("$done");
return 1f;
}
return (float)status.ordinal() / (float)ItemUpdateStatus.values().length;
});
updatedHandlers.put(publishedFileID, updated);
}
@Override
public void onRequestUGCDetails(SteamUGCDetails details, SteamResult result){
@@ -190,7 +213,7 @@ public class SWorkshop implements SteamUGCCallback{
@Override
public void onUGCQueryCompleted(SteamUGCQuery query, int numResultsReturned, int totalMatchingResults, boolean isCachedData, SteamResult result){
Log.info("GET " + query);
Log.info("GET QUERY " + query);
if(detailHandlers.containsKey(query)){
if(numResultsReturned > 0){
@@ -236,38 +259,6 @@ public class SWorkshop implements SteamUGCCallback{
}
}
void updateMap(Map map, SteamPublishedFileID publishedFileID, String changelog){
SteamUGCUpdateHandle h = ugc.startItemUpdate(SVars.steamID, publishedFileID);
Gamemode mode = Gamemode.attack.valid(map) ? Gamemode.attack : Gamemode.survival;
FileHandle mapFile = tmpDirectory.child("map_" + publishedFileID.toString()).child("map.msav");
map.file.copyTo(mapFile);
Log.info(mapFile.parent().absolutePath());
Log.info(map.previewFile().absolutePath());
ugc.setItemTitle(h, map.name());
ugc.setItemDescription(h, map.description());
ugc.setItemTags(h, new String[]{"map", mode.name()});
ugc.setItemVisibility(h, PublishedFileVisibility.Private);
ugc.setItemPreview(h, map.previewFile().absolutePath());
ugc.setItemContent(h, mapFile.parent().absolutePath());
ugc.addItemKeyValueTag(h, "mode", mode.name());
ugc.submitItemUpdate(h, changelog);
ItemUpdateInfo info = new ItemUpdateInfo();
ui.loadfrag.setProgress(() -> {
ItemUpdateStatus status = ugc.getItemUpdateProgress(h, info);
ui.loadfrag.setText("$" + status.name().toLowerCase());
if(status == ItemUpdateStatus.Invalid){
ui.loadfrag.setText("$done");
return 1f;
}
return (float)status.ordinal() / (float)ItemUpdateStatus.values().length;
});
}
@Override
public void onSubmitItemUpdate(SteamPublishedFileID publishedFileID, boolean needsToAcceptWLA, SteamResult result){
ui.loadfrag.hide();
@@ -278,13 +269,10 @@ public class SWorkshop implements SteamUGCCallback{
if(needsToAcceptWLA){
SVars.net.friends.activateGameOverlayToWebPage("https://steamcommunity.com/sharedfiles/workshoplegalagreement");
}
ui.editor.editor.getTags().put("steamid", SteamNativeHandle.getNativeHandle(publishedFileID) + "");
try{
ui.editor.save();
}catch(Exception e){
Log.err(e);
if(updatedHandlers.containsKey(publishedFileID)){
updatedHandlers.get(publishedFileID).run();
}
Events.fire(new MapPublishEvent());
}else{
ui.showErrorMessage(Core.bundle.format("publish.error ", result.name()));
}