Skip to content

Commit

Permalink
1.20.5 port (#10)
Browse files Browse the repository at this point in the history
- For caching, a map's `item_name` takes priority over the `custom_name` if both are present.
- Placeholder trades are no longer empty. (Cosmetic change only.)
- Added custom logic for upgrading caches from older versions
  • Loading branch information
Estecka authored Apr 27, 2024
1 parent d6407fb commit efc835d
Show file tree
Hide file tree
Showing 17 changed files with 181 additions and 77 deletions.
17 changes: 7 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,26 @@ jobs:
matrix:
# Use these Java versions
java: [
17, # Current Java LTS & minimum supported by Minecraft
21, # Current Java LTS & minimum supported by Minecraft
]
# and run on both Linux and Windows
os: [ubuntu-22.04, windows-2022]
runs-on: ${{ matrix.os }}
runs-on: ubuntu-22.04
steps:
- name: checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
uses: gradle/wrapper-validation-action@v2
- name: setup jdk ${{ matrix.java }}
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'microsoft'
- name: make gradle wrapper executable
if: ${{ runner.os != 'Windows' }}
run: chmod +x ./gradlew
- name: build
run: ./gradlew build
- name: capture build artifacts
if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS
uses: actions/upload-artifact@v3
if: ${{ matrix.java == '21' }} # Only upload artifacts built from latest java
uses: actions/upload-artifact@v4
with:
name: Artifacts
path: build/libs/
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
contents: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: Artifacts
path: ./
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ classes/
.vscode/
bin/
.classpath
.factorypath
.project

# macos
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ To prevent daily rerolls from throwing away endless amounts of unsold maps, thos
Cartographers will remember each map they sell, and offer it again it the next time the same map trade comes up.
The gamerule `shiftingWares.allowMapReroll` (disabled by default) will allow them to forget a map, after it has been sold at least once.

The map's item name, or its translation key, is used to tell apart different types of maps.
The map's item name, (or preferably, its translation key), is used to tell apart different types of maps.
Since 1.20.5, the `item_name` is preferred over the `custom_name`.


## Technical details
Expand Down
21 changes: 14 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
plugins {
id 'fabric-loom' version '1.4-SNAPSHOT'
id 'fabric-loom' version '1.6-SNAPSHOT'
id 'maven-publish'
}

loom {
accessWidenerPath = file("src/main/resources/shifting-wares.accesswidener")
}

version = "${project.mod_version}+${project.minecraft_version}"
group = project.maven_group

Expand All @@ -26,7 +30,7 @@ dependencies {

// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"

// Uncomment the following line to enable the deprecated Fabric API modules.
// These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time.

Expand All @@ -39,10 +43,12 @@ processResources {
filesMatching("fabric.mod.json") {
expand "version": project.version
}

exclude "**/*.xcf"
}

tasks.withType(JavaCompile).configureEach {
it.options.release = 17
it.options.release = 21
}

java {
Expand All @@ -51,8 +57,8 @@ java {
// If you remove this line, sources will not be generated.
withSourcesJar()

sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

jar {
Expand All @@ -64,7 +70,8 @@ jar {
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
create("mavenJava", MavenPublication) {
artifactId = project.archives_base_name
from components.java
}
}
Expand All @@ -76,4 +83,4 @@ publishing {
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}
}
12 changes: 9 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,16 @@ Initial release
- _Regression: Restocks cause a crash if Deplete Reroll is disabled._

# v2
## 2.0.0
## 2.0
- Made some methods and types available through an API.
- Added an entrypoint to define villager trades from outside mods
## 2.1.0
## 2.1
### 2.1.0
- Reimplemented Trade-Rebalance support in a backward-compatible way
## 2.1.1
### 2.1.1
- Fixed a crash that would occur upon restock if Depleted Reroll is disabled.
## 2.2
- Updated for 1.20.5:
- For caching, a map's `item_name` takes priority over the `custom_name` if both are present.
- Placeholder trades are no longer empty. (Cosmetic change only.)
- Added custom logic for upgrading caches from before 1.20.5
10 changes: 5 additions & 5 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ org.gradle.parallel=true

# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.20.2
yarn_mappings=1.20.2+build.4
loader_version=0.15.3
minecraft_version=1.20.5
yarn_mappings=1.20.5+build.1
loader_version=0.15.10

#Fabric api
fabric_version=0.91.2+1.20.2
fabric_version=0.97.6+1.20.5

# Mod Properties
mod_version=2.1.1
mod_version=2.2.0
maven_group=tk.estecka.shiftingwares
archives_base_name=shifting-wares
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
20 changes: 10 additions & 10 deletions gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

Expand All @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

Expand Down
16 changes: 16 additions & 0 deletions port.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Minecraft Code Breaking Changes
### 1.19.4
Current master

### 1.20.2
#### Worked Around:
- Need to handle the experimental trade rebalance: build for 1.20.2 and check that the feature's identifier exists before executing related code.

### 1.20.5
#### No Workaround:
- `ItemStack::hasCustomName` and `FilledMapItem::getMapId` were replaced with DataComponents.
- Exploration maps now use an `item_name` instead of a `custom_name`. Map caches from older versions are not automatically upgraded by minecraft.
- `TradeOffer` now takes the price as a `TradeItem` instead of an `ItemStack`. The second price is also an `Optional`
#### Possible Workaround:
- `ItemStack::writeToNbt` and `readFromNbt` were removed or changed: Use `ItemStack::CODEC` instead.
- Trade offers no longer support selling or buying air: Use some placeholder items instead.
85 changes: 65 additions & 20 deletions src/main/java/tk/estecka/shiftingwares/MapTradesCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@
import java.util.Optional;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import com.mojang.serialization.Dynamic;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.MapIdComponent;
import net.minecraft.datafixer.fix.ItemStackComponentizationFix;
import net.minecraft.datafixer.fix.ItemStackCustomNameToItemNameFix;
import net.minecraft.entity.Entity;
import net.minecraft.item.FilledMapItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.NbtString;
import net.minecraft.text.Text;
import net.minecraft.text.TextContent;
import net.minecraft.text.TranslatableTextContent;
import net.minecraft.village.TradeOffer;
Expand All @@ -24,37 +30,46 @@
public class MapTradesCache
implements PersistentItemCache
{
static public final int DATA_FORMAT = 1;
static public final int DATA_FORMAT = 2;
static public final String FORMAT_KEY = "shifting-wares:data_format";
static public final String MAPID_CACHE = "shifting-wares:created_maps";
static public final String SOLD_ITEMS = "shifting-wares:sold_items";

private final Map<String, ItemStack> cachedItems = new HashMap<String,ItemStack>();
private final Set<String> soldItems = new HashSet<>();

static public @Nullable Integer GetRawMapId(ItemStack stack){
MapIdComponent component = stack.get(DataComponentTypes.MAP_ID);
return (component==null) ? null : component.id();
}

/**
* @return null if the item needs not or cannot be cached.
*/
@Nullable
static public String FindCacheKey(ItemStack item){
static public @Nullable String FindCacheKey(ItemStack item){
if (!item.isOf(Items.FILLED_MAP))
return null;

if (!item.hasCustomName()){
ShiftingWares.LOGGER.error("Unable to identify map#{} with no name:\n{}", FilledMapItem.getMapId(item), item);
Text name = item.get(DataComponentTypes.ITEM_NAME);
if (name == null){
name = item.get(DataComponentTypes.CUSTOM_NAME);
if (name != null)
ShiftingWares.LOGGER.warn("A map was found that uses a CUSTOM_NAME but has no ITEM_NAME: \"{}\"", name);
}

if (name == null){
ShiftingWares.LOGGER.error("Unable to identify map#{} with no name:\n{}", GetRawMapId(item), item);
return null;
}

TextContent fullName = item.getName().getContent();
String key;
TextContent fullName = name.getContent();
if (fullName instanceof TranslatableTextContent translatable)
key = translatable.getKey();
return translatable.getKey();
else {
ShiftingWares.LOGGER.error("Map#{} name is not a translation key: {} {}", FilledMapItem.getMapId(item), fullName.getClass(), fullName);
key = item.getName().getString();
ShiftingWares.LOGGER.error("Map#{} name is not a translation key: {} {}", GetRawMapId(item), fullName.getClass(), fullName);
return item.getName().getString();
}

return key;
}

/**
Expand Down Expand Up @@ -84,12 +99,12 @@ public Optional<ItemStack> GetCachedItem(String key){
}

public void AddCachedItem(String key, ItemStack mapItem){
Integer neoId=FilledMapItem.getMapId(mapItem);
Integer neoId = GetRawMapId(mapItem);
if (!cachedItems.containsKey(key))
ShiftingWares.LOGGER.info("New map trade: #{} @ {}", neoId, key);
else
{
Integer oldId=FilledMapItem.getMapId(cachedItems.get(key));
Integer oldId = GetRawMapId(cachedItems.get(key));
if (soldItems.contains(key))
ShiftingWares.LOGGER.info("New map trade #{}->#{} @ {}", oldId, neoId, key);
else if (Objects.equals(neoId, oldId))
Expand Down Expand Up @@ -124,12 +139,12 @@ public void FillCacheFromTrades(TradeOfferList offers){

if (offer.hasBeenUsed()) {
this.soldItems.add(cacheKey);
ShiftingWares.LOGGER.info("Marked map as sold: #{} @ {}", FilledMapItem.getMapId(sellItem), cacheKey);
ShiftingWares.LOGGER.info("Marked map as sold: #{} @ {}", GetRawMapId(sellItem), cacheKey);
}

var oldItem = this.GetCachedItem(cacheKey);
if (oldItem.isEmpty() || !ItemStack.areEqual(sellItem, oldItem.get())){
ShiftingWares.LOGGER.warn("Caught a map trade that wasn't properly cached: #{} @ {}", FilledMapItem.getMapId(sellItem), cacheKey);
ShiftingWares.LOGGER.warn("Caught a map trade that wasn't properly cached: #{} @ {}", GetRawMapId(sellItem), cacheKey);
this.AddCachedItem(cacheKey, sellItem);
}
}
Expand All @@ -140,14 +155,40 @@ public void FillCacheFromTrades(TradeOfferList offers){
/* # Serialization */
/******************************************************************************/

// 1.20.4 -> 1.20.5 upgrade
static public Dynamic<?> ComponentizeLegacyItem(Dynamic<?> dynamic){
var optStackData = ItemStackComponentizationFix.StackData.fromDynamic(dynamic);
if (optStackData.isEmpty())
return dynamic;

var stackData = optStackData.get();
ItemStackComponentizationFix.fixStack(optStackData.get(), dynamic);
dynamic = stackData.finalize();

var components = dynamic.get("components").result();
if (components.isPresent()){
dynamic = dynamic.set("components", ItemStackCustomNameToItemNameFix.fixExplorerMaps(components.get()));
}

return dynamic;
}


public void ReadMapCacheFromNbt(NbtCompound nbt){
NbtCompound nbtcache = nbt.getCompound(MAPID_CACHE);
NbtList nbtsold = nbt.getList(SOLD_ITEMS, NbtElement.STRING_TYPE);
int format = nbt.getInt(FORMAT_KEY);

if (nbtcache != null)
for (String key : nbtcache.getKeys()){
ItemStack item = ItemStack.fromNbt(nbtcache.getCompound(key));
this.cachedItems.put(key, item);
Dynamic<?> dynamic = new Dynamic<>(NbtOps.INSTANCE, nbtcache.getCompound(key));
if (format < 2)
dynamic = ComponentizeLegacyItem(dynamic);

ItemStack.CODEC.parse(dynamic)
.resultOrPartial(err -> ShiftingWares.LOGGER.error("Unabled to decode cached item! @{}\n", key, err))
.ifPresent( item -> this.cachedItems.put(key, item))
;
}

if (nbtsold != null)
Expand All @@ -161,8 +202,12 @@ public NbtCompound WriteMapCacheToNbt(NbtCompound nbt){
NbtCompound nbtcache = new NbtCompound();
NbtList nbtsold = new NbtList();

for (var pair : this.cachedItems.entrySet())
nbtcache.put(pair.getKey(), pair.getValue().writeNbt(new NbtCompound()));
for (var pair : this.cachedItems.entrySet()){
ItemStack.CODEC.encodeStart(NbtOps.INSTANCE, pair.getValue())
.resultOrPartial(err -> ShiftingWares.LOGGER.error("Unable to encode cached item! @{}\n{}", pair.getKey(), err))
.ifPresent(nbtItem -> nbtcache.put(pair.getKey(), nbtItem))
;
}
for (String key : this.soldItems)
nbtsold.add(NbtString.of(key));

Expand Down
Loading

0 comments on commit efc835d

Please sign in to comment.