Workshop map updates, removal detection
This commit is contained in:
@@ -178,6 +178,7 @@ warning = Warning.
|
|||||||
confirm = Confirm
|
confirm = Confirm
|
||||||
delete = Delete
|
delete = Delete
|
||||||
view.workshop = View In Workshop
|
view.workshop = View In Workshop
|
||||||
|
workshop.listing = Edit Workshop Listing
|
||||||
ok = OK
|
ok = OK
|
||||||
open = Open
|
open = Open
|
||||||
customize = Customize Rules
|
customize = Customize Rules
|
||||||
@@ -215,7 +216,12 @@ map.nospawn.pvp = This map does not have any enemy cores for player to spawn int
|
|||||||
map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor.
|
map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor.
|
||||||
map.invalid = Error loading map: corrupted or invalid map file.
|
map.invalid = Error loading map: corrupted or invalid map file.
|
||||||
map.publish.error = Error publishing map: {0}
|
map.publish.error = Error publishing map: {0}
|
||||||
|
map.update = Update Map
|
||||||
|
map.load.error = Error fetching workshop details: {0}
|
||||||
|
map.missing = This map has been deleted or moved.\n[lightgray]The workshop listing has now been automatically un-linked from the map.
|
||||||
map.publish.confirm = Are you sure you want to publish this map?\n\n[lightgray]Make sure you agree to the Workshop EULA first, or your maps will not show up!
|
map.publish.confirm = Are you sure you want to publish this map?\n\n[lightgray]Make sure you agree to the Workshop EULA first, or your maps will not show up!
|
||||||
|
map.menu = Select what you would like to do with this map.
|
||||||
|
map.changelog = Changelog (optional):
|
||||||
eula = Steam EULA
|
eula = Steam EULA
|
||||||
map.publish = Map published.
|
map.publish = Map published.
|
||||||
map.publishing = [accent]Publishing map...
|
map.publishing = [accent]Publishing map...
|
||||||
@@ -1071,7 +1077,7 @@ block.core-foundation.description = The second version of the core. Better armor
|
|||||||
block.core-nucleus.description = The third and final iteration of the core capsule. Extremely well armored. Stores massive amounts of resources.
|
block.core-nucleus.description = The third and final iteration of the core capsule. Extremely well armored. Stores massive amounts of resources.
|
||||||
block.vault.description = Stores a large amount of items of each type. An unloader block can be used to retrieve items from the vault.
|
block.vault.description = Stores a large amount of items of each type. An unloader block can be used to retrieve items from the vault.
|
||||||
block.container.description = Stores a small amount of items of each type. An unloader block can be used to retrieve items from the container.
|
block.container.description = Stores a small amount of items of each type. An unloader block can be used to retrieve items from the container.
|
||||||
block.unloader.description = Unloads items from a container, vault or core onto a conveyor or directly into an adjacent block. The type of item to be unloaded can be changed by tapping.
|
block.unloader.description = Unloads items from any nearby non-transportation block. The type of item to be unloaded can be changed by tapping.
|
||||||
block.launch-pad.description = Launches batches of items without any need for a core launch.
|
block.launch-pad.description = Launches batches of items without any need for a core launch.
|
||||||
block.launch-pad-large.description = An improved version of the launch pad. Stores more items. Launches more frequently.
|
block.launch-pad-large.description = An improved version of the launch pad. Stores more items. Launches more frequently.
|
||||||
block.duo.description = A small, cheap turret. Useful against ground units.
|
block.duo.description = A small, cheap turret. Useful against ground units.
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ public interface Platform{
|
|||||||
/** Steam: View a map listing on the workshop.*/
|
/** Steam: View a map listing on the workshop.*/
|
||||||
default void viewMapListing(String mapid){}
|
default void viewMapListing(String mapid){}
|
||||||
|
|
||||||
|
/** Steam: View map workshop info, removing the map ID tag if its listing is deleted.
|
||||||
|
* Also presents the option to update the map. */
|
||||||
|
default void viewMapListingInfo(Map map){}
|
||||||
|
|
||||||
/** Steam: Open workshop for maps.*/
|
/** Steam: Open workshop for maps.*/
|
||||||
default void openWorkshop(){}
|
default void openWorkshop(){}
|
||||||
|
|
||||||
|
|||||||
@@ -147,13 +147,13 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
|
|
||||||
if(steam){
|
if(steam){
|
||||||
menu.cont.addImageTextButton("$editor.publish.workshop", Icon.linkSmall, () -> {
|
menu.cont.addImageTextButton("$editor.publish.workshop", Icon.linkSmall, () -> {
|
||||||
|
Map map = save();
|
||||||
|
|
||||||
if(editor.getTags().containsKey("steamid")){
|
if(editor.getTags().containsKey("steamid")){
|
||||||
platform.viewMapListing(editor.getTags().get("steamid"));
|
platform.viewMapListingInfo(map);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map map = save();
|
|
||||||
|
|
||||||
if(map == null) return;
|
if(map == null) return;
|
||||||
|
|
||||||
if(map.tags.get("description", "").length() < 4){
|
if(map.tags.get("description", "").length() < 4){
|
||||||
@@ -167,7 +167,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
platform.publishMap(map);
|
platform.publishMap(map);
|
||||||
}).padTop(-3).size(swidth * 2f + 10, 60f).update(b -> b.setText(editor.getTags().containsKey("steamid") ? "$view.workshop" : "$editor.publish.workshop"));
|
}).padTop(-3).size(swidth * 2f + 10, 60f).update(b -> b.setText(editor.getTags().containsKey("steamid") ? editor.getTags().get("author").equals(player.name) ? "$workshop.listing" : "$view.workshop" : "$editor.publish.workshop"));
|
||||||
|
|
||||||
menu.cont.row();
|
menu.cont.row();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ public class Bar extends Element{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reset(float value){
|
||||||
|
this.value = lastValue = blink = value;
|
||||||
|
}
|
||||||
|
|
||||||
public void set(Supplier<String> name, FloatProvider fraction, Color color){
|
public void set(Supplier<String> name, FloatProvider fraction, Color color){
|
||||||
this.fraction = fraction;
|
this.fraction = fraction;
|
||||||
this.lastValue = fraction.get();
|
this.lastValue = fraction.get();
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public class LoadingFragment extends Fragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setProgress(FloatProvider progress){
|
public void setProgress(FloatProvider progress){
|
||||||
|
bar.reset(0f);
|
||||||
bar.visible(true);
|
bar.visible(true);
|
||||||
bar.set(() -> ((int)(progress.get() * 100) + "%"), progress, Pal.accent);
|
bar.set(() -> ((int)(progress.get() * 100) + "%"), progress, Pal.accent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import static io.anuke.mindustry.Vars.*;
|
|||||||
public class DesktopLauncher extends ClientLauncher{
|
public class DesktopLauncher extends ClientLauncher{
|
||||||
public final static String discordID = "610508934456934412";
|
public final static String discordID = "610508934456934412";
|
||||||
|
|
||||||
boolean useDiscord = OS.is64Bit, showConsole = false;
|
boolean useDiscord = OS.is64Bit, showConsole = OS.getPropertyNotNull("user.name").equals("anuke");
|
||||||
|
|
||||||
public static void main(String[] arg){
|
public static void main(String[] arg){
|
||||||
try{
|
try{
|
||||||
@@ -215,6 +215,11 @@ public class DesktopLauncher extends ClientLauncher{
|
|||||||
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + mapid);
|
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + mapid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void viewMapListingInfo(Map map){
|
||||||
|
SVars.workshop.viewMapListingInfo(map);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetProvider getNet(){
|
public NetProvider getNet(){
|
||||||
if(steam && SVars.net == null) SVars.net = new SNet(new ArcNetImpl());
|
if(steam && SVars.net == null) SVars.net = new SNet(new ArcNetImpl());
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import com.codedisaster.steamworks.SteamUGC.*;
|
|||||||
import io.anuke.arc.*;
|
import io.anuke.arc.*;
|
||||||
import io.anuke.arc.collection.*;
|
import io.anuke.arc.collection.*;
|
||||||
import io.anuke.arc.files.*;
|
import io.anuke.arc.files.*;
|
||||||
|
import io.anuke.arc.function.*;
|
||||||
|
import io.anuke.arc.scene.ui.*;
|
||||||
import io.anuke.arc.util.*;
|
import io.anuke.arc.util.*;
|
||||||
import io.anuke.mindustry.game.EventType.*;
|
import io.anuke.mindustry.game.EventType.*;
|
||||||
import io.anuke.mindustry.game.*;
|
import io.anuke.mindustry.game.*;
|
||||||
@@ -20,6 +22,7 @@ public class SWorkshop implements SteamUGCCallback{
|
|||||||
|
|
||||||
private Map lastMap;
|
private Map lastMap;
|
||||||
private Array<FileHandle> mapFiles;
|
private Array<FileHandle> mapFiles;
|
||||||
|
private ObjectMap<SteamUGCQuery, BiConsumer<SteamUGCDetails, SteamResult>> detailHandlers = new ObjectMap<>();
|
||||||
|
|
||||||
public SWorkshop(){
|
public SWorkshop(){
|
||||||
int items = ugc.getNumSubscribedItems();
|
int items = ugc.getNumSubscribedItems();
|
||||||
@@ -51,6 +54,8 @@ public class SWorkshop implements SteamUGCCallback{
|
|||||||
|
|
||||||
//update author name when publishing
|
//update author name when publishing
|
||||||
map.tags.put("author", SVars.net.friends.getPersonaName());
|
map.tags.put("author", SVars.net.friends.getPersonaName());
|
||||||
|
ui.editor.editor.getTags().put("author", map.tags.get("author"));
|
||||||
|
ui.editor.save();
|
||||||
|
|
||||||
FloatingDialog dialog = new FloatingDialog("$confirm");
|
FloatingDialog dialog = new FloatingDialog("$confirm");
|
||||||
dialog.setFillParent(false);
|
dialog.setFillParent(false);
|
||||||
@@ -70,9 +75,101 @@ public class SWorkshop implements SteamUGCCallback{
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void viewMapListingInfo(Map map){
|
||||||
|
|
||||||
|
String id = map.tags.get("steamid");
|
||||||
|
long handle = Strings.parseLong(id, -1);
|
||||||
|
SteamPublishedFileID fid = new SteamPublishedFileID(handle);
|
||||||
|
|
||||||
|
Log.info("Requesting map listing view; id = " + id);
|
||||||
|
|
||||||
|
ui.loadfrag.show();
|
||||||
|
SteamUGCQuery query = ugc.createQueryUGCDetailsRequest(fid);
|
||||||
|
Log.info("POST " + query);
|
||||||
|
|
||||||
|
detailHandlers.put(query, (details, result) -> {
|
||||||
|
ui.loadfrag.hide();
|
||||||
|
|
||||||
|
Log.info("Map listing result: " + result + " " + details.getResult() + " " + details.getFileName() + " " + details.getTitle());
|
||||||
|
|
||||||
|
if(result == SteamResult.OK){
|
||||||
|
if(details.getResult() == SteamResult.OK){
|
||||||
|
if(details.getOwnerID().equals(SVars.user.user.getSteamID())){
|
||||||
|
|
||||||
|
FloatingDialog dialog = new FloatingDialog("$editor.mapinfo");
|
||||||
|
dialog.setFillParent(false);
|
||||||
|
dialog.cont.add("$map.menu").pad(20f);
|
||||||
|
dialog.addCloseButton();
|
||||||
|
|
||||||
|
dialog.buttons.addImageTextButton("$view.workshop", Icon.linkSmall, () -> {
|
||||||
|
platform.viewMapListing(id);
|
||||||
|
dialog.hide();
|
||||||
|
}).size(210f, 64f);
|
||||||
|
|
||||||
|
dialog.buttons.addImageTextButton("$map.update", Icon.upgradeSmall, () -> {
|
||||||
|
new FloatingDialog("$map.update"){{
|
||||||
|
setFillParent(false);
|
||||||
|
cont.margin(10).add("$map.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("$map.publishing");
|
||||||
|
lastMap = map;
|
||||||
|
updateMap(map, details.getPublishedFileID(), 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");
|
||||||
|
}else{
|
||||||
|
ui.showErrorMessage(Core.bundle.format("map.load.error", result.name()));
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
ui.showErrorMessage(Core.bundle.format("map.load.error", result.name()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ugc.sendQueryUGCRequest(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestUGCDetails(SteamUGCDetails details, SteamResult result){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUGCQueryCompleted(SteamUGCQuery query, int numResultsReturned, int totalMatchingResults, boolean isCachedData, SteamResult result){
|
public void onUGCQueryCompleted(SteamUGCQuery query, int numResultsReturned, int totalMatchingResults, boolean isCachedData, SteamResult result){
|
||||||
|
Log.info("GET " + query);
|
||||||
|
|
||||||
|
if(detailHandlers.containsKey(query)){
|
||||||
|
if(numResultsReturned > 0){
|
||||||
|
SteamUGCDetails details = new SteamUGCDetails();
|
||||||
|
ugc.getQueryUGCResult(query, 0, details);
|
||||||
|
detailHandlers.get(query).accept(details, result);
|
||||||
|
}else{
|
||||||
|
detailHandlers.get(query).accept(null, SteamResult.FileNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
detailHandlers.remove(query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -90,11 +187,6 @@ public class SWorkshop implements SteamUGCCallback{
|
|||||||
Log.info("Item unsubscribed from {0}", info.getFolder());
|
Log.info("Item unsubscribed from {0}", info.getFolder());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestUGCDetails(SteamUGCDetails details, SteamResult result){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateItem(SteamPublishedFileID publishedFileID, boolean needsToAcceptWLA, SteamResult result){
|
public void onCreateItem(SteamPublishedFileID publishedFileID, boolean needsToAcceptWLA, SteamResult result){
|
||||||
if(lastMap == null){
|
if(lastMap == null){
|
||||||
@@ -108,35 +200,7 @@ public class SWorkshop implements SteamUGCCallback{
|
|||||||
Log.info("Create item {0} result {1} {2}", SteamNativeHandle.getNativeHandle(publishedFileID), result, needsToAcceptWLA);
|
Log.info("Create item {0} result {1} {2}", SteamNativeHandle.getNativeHandle(publishedFileID), result, needsToAcceptWLA);
|
||||||
|
|
||||||
if(result == SteamResult.OK){
|
if(result == SteamResult.OK){
|
||||||
SteamUGCUpdateHandle h = ugc.startItemUpdate(SVars.steamID, publishedFileID);
|
updateMap(map, publishedFileID, "<Map Created>");
|
||||||
|
|
||||||
Gamemode mode = Gamemode.attack.valid(map) ? Gamemode.attack : Gamemode.survival;
|
|
||||||
FileHandle mapFile = tmpDirectory.child("map_" + publishedFileID.toString()).child("map.msav");
|
|
||||||
lastMap.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, "Map created");
|
|
||||||
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
}else{
|
}else{
|
||||||
ui.showErrorMessage(Core.bundle.format("map.publish.error ", result.name()));
|
ui.showErrorMessage(Core.bundle.format("map.publish.error ", result.name()));
|
||||||
}
|
}
|
||||||
@@ -144,6 +208,38 @@ public class SWorkshop implements SteamUGCCallback{
|
|||||||
lastMap = null;
|
lastMap = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
lastMap.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
|
@Override
|
||||||
public void onSubmitItemUpdate(SteamPublishedFileID publishedFileID, boolean needsToAcceptWLA, SteamResult result){
|
public void onSubmitItemUpdate(SteamPublishedFileID publishedFileID, boolean needsToAcceptWLA, SteamResult result){
|
||||||
ui.loadfrag.hide();
|
ui.loadfrag.hide();
|
||||||
|
|||||||
Reference in New Issue
Block a user