Skip to content

Commit 30a60ce

Browse files
committed
Update docs for new extensions, fix errors, add villager trade API
1 parent 79c14ec commit 30a60ce

8 files changed

Lines changed: 169 additions & 4 deletions

File tree

releases/pyjavabridge-dev.jar

6.8 KB
Binary file not shown.

src/main/java/com/pyjavabridge/BridgeInstance.java

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,71 @@ private List<Object> preprocessTeleportArgs(Entity entity, List<Object> args) {
11101110
return args;
11111111
}
11121112

1113+
private Map<String, Object> serializeMerchantRecipe(org.bukkit.inventory.MerchantRecipe recipe) {
1114+
Map<String, Object> map = new java.util.LinkedHashMap<>();
1115+
map.put("result", recipe.getResult().serialize());
1116+
List<Map<String, Object>> ingredients = new ArrayList<>();
1117+
for (ItemStack ingredient : recipe.getIngredients()) {
1118+
ingredients.add(ingredient.serialize());
1119+
}
1120+
map.put("ingredients", ingredients);
1121+
map.put("maxUses", recipe.getMaxUses());
1122+
map.put("uses", recipe.getUses());
1123+
map.put("experienceReward", recipe.hasExperienceReward());
1124+
map.put("villagerExperience", recipe.getVillagerExperience());
1125+
map.put("priceMultiplier", recipe.getPriceMultiplier());
1126+
map.put("demand", recipe.getDemand());
1127+
map.put("specialPrice", recipe.getSpecialPrice());
1128+
return map;
1129+
}
1130+
1131+
@SuppressWarnings("unchecked")
1132+
private org.bukkit.inventory.MerchantRecipe deserializeMerchantRecipe(Map<String, Object> map) {
1133+
if (map == null) return null;
1134+
// Deserialize result item
1135+
Object resultObj = map.get("result");
1136+
ItemStack result;
1137+
if (resultObj instanceof Map<?, ?> resultMap) {
1138+
result = ItemStack.deserialize(toStringKeyMap(resultMap));
1139+
} else {
1140+
return null;
1141+
}
1142+
int maxUses = map.containsKey("maxUses") ? ((Number) map.get("maxUses")).intValue() : 1;
1143+
boolean experienceReward = map.containsKey("experienceReward") ? Boolean.TRUE.equals(map.get("experienceReward")) : true;
1144+
int villagerExperience = map.containsKey("villagerExperience") ? ((Number) map.get("villagerExperience")).intValue() : 0;
1145+
float priceMultiplier = map.containsKey("priceMultiplier") ? ((Number) map.get("priceMultiplier")).floatValue() : 0.0f;
1146+
int demand = map.containsKey("demand") ? ((Number) map.get("demand")).intValue() : 0;
1147+
int specialPrice = map.containsKey("specialPrice") ? ((Number) map.get("specialPrice")).intValue() : 0;
1148+
int uses = map.containsKey("uses") ? ((Number) map.get("uses")).intValue() : 0;
1149+
1150+
org.bukkit.inventory.MerchantRecipe recipe = new org.bukkit.inventory.MerchantRecipe(
1151+
result, uses, maxUses, experienceReward, villagerExperience, priceMultiplier, demand, specialPrice
1152+
);
1153+
1154+
// Deserialize ingredients
1155+
Object ingredientsObj = map.get("ingredients");
1156+
if (ingredientsObj instanceof List<?> ingredientsList) {
1157+
List<ItemStack> ingredients = new ArrayList<>();
1158+
for (Object ingObj : ingredientsList) {
1159+
if (ingObj instanceof Map<?, ?> ingMap) {
1160+
ItemStack ing = ItemStack.deserialize(toStringKeyMap(ingMap));
1161+
ingredients.add(ing);
1162+
}
1163+
}
1164+
recipe.setIngredients(ingredients);
1165+
}
1166+
1167+
return recipe;
1168+
}
1169+
1170+
private Map<String, Object> toStringKeyMap(Map<?, ?> map) {
1171+
Map<String, Object> result = new java.util.LinkedHashMap<>();
1172+
for (Map.Entry<?, ?> entry : map.entrySet()) {
1173+
result.put(String.valueOf(entry.getKey()), entry.getValue());
1174+
}
1175+
return result;
1176+
}
1177+
11131178
private Location toLocation(Object arg, Entity context) {
11141179
if (arg instanceof Location loc) return loc;
11151180
if (arg instanceof List<?> list && list.size() >= 3) {
@@ -1885,6 +1950,51 @@ private Object invokeMobMethod(Entity entity, String method, List<Object> args)
18851950
goals.removeAllGoals(mob);
18861951
return null;
18871952
}
1953+
// ── Villager trades ──────────────────────────────────────
1954+
case "getRecipes" -> {
1955+
if (!(mob instanceof org.bukkit.entity.AbstractVillager villager)) return UNHANDLED;
1956+
List<org.bukkit.inventory.MerchantRecipe> recipes = villager.getRecipes();
1957+
List<Map<String, Object>> result = new ArrayList<>();
1958+
for (org.bukkit.inventory.MerchantRecipe recipe : recipes) {
1959+
result.add(serializeMerchantRecipe(recipe));
1960+
}
1961+
return result;
1962+
}
1963+
case "getRecipeCount" -> {
1964+
if (!(mob instanceof org.bukkit.entity.AbstractVillager villager)) return UNHANDLED;
1965+
return villager.getRecipeCount();
1966+
}
1967+
case "setRecipes" -> {
1968+
if (!(mob instanceof org.bukkit.entity.AbstractVillager villager)) return UNHANDLED;
1969+
if (args.isEmpty()) return null;
1970+
@SuppressWarnings("unchecked")
1971+
List<Map<String, Object>> recipeMaps = (List<Map<String, Object>>) args.get(0);
1972+
List<org.bukkit.inventory.MerchantRecipe> recipes = new ArrayList<>();
1973+
for (Map<String, Object> map : recipeMaps) {
1974+
org.bukkit.inventory.MerchantRecipe recipe = deserializeMerchantRecipe(map);
1975+
if (recipe != null) recipes.add(recipe);
1976+
}
1977+
villager.setRecipes(recipes);
1978+
return null;
1979+
}
1980+
case "addRecipe" -> {
1981+
if (!(mob instanceof org.bukkit.entity.AbstractVillager villager)) return UNHANDLED;
1982+
if (args.isEmpty()) return null;
1983+
@SuppressWarnings("unchecked")
1984+
Map<String, Object> recipeMap = (Map<String, Object>) args.get(0);
1985+
org.bukkit.inventory.MerchantRecipe recipe = deserializeMerchantRecipe(recipeMap);
1986+
if (recipe != null) {
1987+
List<org.bukkit.inventory.MerchantRecipe> recipes = new ArrayList<>(villager.getRecipes());
1988+
recipes.add(recipe);
1989+
villager.setRecipes(recipes);
1990+
}
1991+
return null;
1992+
}
1993+
case "clearRecipes" -> {
1994+
if (!(mob instanceof org.bukkit.entity.AbstractVillager villager)) return UNHANDLED;
1995+
villager.setRecipes(new ArrayList<>());
1996+
return null;
1997+
}
18881998
}
18891999
return UNHANDLED;
18902000
}

src/main/resources/python/bridge/__init__.pyi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,16 @@ class Villager(Entity):
366366
def villager_experience(self) -> int: ...
367367
@villager_experience.setter
368368
def villager_experience(self, value: int) -> None: ...
369+
@property
370+
def recipes(self) -> list[dict]: ...
371+
@recipes.setter
372+
def recipes(self, value: list[dict]) -> None: ...
373+
@property
374+
def recipe_count(self) -> int: ...
375+
def add_recipe(self, result: dict, ingredients: list[dict], max_uses: int = 1,
376+
experience_reward: bool = True, villager_experience: int = 0,
377+
price_multiplier: float = 0.0, demand: int = 0, special_price: int = 0) -> None: ...
378+
def clear_recipes(self) -> None: ...
369379

370380
class ItemFrame(Entity):
371381
@property

src/main/resources/python/bridge/extensions/guild.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def broadcast(self, message: str):
134134
"""Send a message to all online guild members."""
135135
async def _send():
136136
from bridge import server
137-
online = await server.players
137+
online = server.players
138138
for p in online:
139139
if str(p.uuid) in self._members:
140140
await p.send_message(f"§6[Guild] §f{message}")

src/main/resources/python/bridge/extensions/leaderboard.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ async def _refresh(self):
6060
from bridge import server
6161
if self._hologram is None or self._get_metric is None:
6262
return
63-
online = await server.players
63+
online = server.players
6464
scores: List[tuple[str, float]] = []
6565
for p in online:
6666
try:

src/main/resources/python/bridge/extensions/npc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ async def _range_check_loop(self):
154154
if npc_loc is None:
155155
await server.after(20)
156156
continue
157-
online = await server.players
157+
online = server.players
158158
for p in online:
159159
puuid = str(p.uuid)
160160
try:

src/main/resources/python/bridge/extensions/region.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ async def _track_loop(cls):
7979
from bridge import server
8080
while True:
8181
try:
82-
online = await server.players
82+
online = server.players
8383
for region in list(cls._all_regions):
8484
for p in online:
8585
puuid = str(p.uuid)

src/main/resources/python/bridge/wrappers.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,51 @@ def villager_experience(self) -> int:
874874
def villager_experience(self, value: int):
875875
self._call("setVillagerExperience", int(value))
876876

877+
@property
878+
def recipes(self) -> list[dict]:
879+
"""Get the villager's trade recipes."""
880+
return self._call_sync("getRecipes")
881+
882+
@recipes.setter
883+
def recipes(self, value: list[dict]):
884+
"""Set the villager's trade recipes."""
885+
self._call("setRecipes", value)
886+
887+
@property
888+
def recipe_count(self) -> int:
889+
"""Get the number of trade recipes."""
890+
return self._call_sync("getRecipeCount")
891+
892+
def add_recipe(self, result: dict, ingredients: list[dict], max_uses: int = 1,
893+
experience_reward: bool = True, villager_experience: int = 0,
894+
price_multiplier: float = 0.0, demand: int = 0, special_price: int = 0):
895+
"""Add a trade recipe to this villager.
896+
897+
Args:
898+
result: Serialized ItemStack dict for the result item.
899+
ingredients: List of serialized ItemStack dicts (1-2 items).
900+
max_uses: Maximum number of uses before the trade locks.
901+
experience_reward: Whether the trade rewards experience.
902+
villager_experience: Experience given to the villager.
903+
price_multiplier: Price multiplier based on demand.
904+
demand: Current demand for this trade.
905+
special_price: Special price adjustment.
906+
"""
907+
self._call("addRecipe", {
908+
"result": result,
909+
"ingredients": ingredients,
910+
"maxUses": max_uses,
911+
"experienceReward": experience_reward,
912+
"villagerExperience": villager_experience,
913+
"priceMultiplier": price_multiplier,
914+
"demand": demand,
915+
"specialPrice": special_price,
916+
})
917+
918+
def clear_recipes(self):
919+
"""Remove all trade recipes from this villager."""
920+
self._call("clearRecipes")
921+
877922

878923
class ItemFrame(Entity):
879924
"""ItemFrame entity."""

0 commit comments

Comments
 (0)