diff --git a/ExampleMod/ExampleMod.cs b/ExampleMod/ExampleMod.cs
index a8d1097..3fb0c06 100644
--- a/ExampleMod/ExampleMod.cs
+++ b/ExampleMod/ExampleMod.cs
@@ -246,7 +246,8 @@ public class ExampleMod : IMod
.Icon("examplemod:block/ruby_sand")
.Name(Text.Translatable("block.examplemod.ruby_sand"))
.RequiredTool(ToolType.Shovel)
- .InCreativeTab(CreativeTab.BuildingBlocks));
+ .InCreativeTab(CreativeTab.BuildingBlocks)
+ .Prepend());
RubyStoneSlab = (RegisteredSlabBlock)Registry.Block.Register("examplemod:ruby_stone_slab",
new SlabBlock(),
@@ -371,7 +372,8 @@ public class ExampleMod : IMod
.AttackDamage(1.0f)
.Icon("examplemod:item/ruby_hoe")
.Name(Text.Translatable("item.examplemod.ruby_hoe"))
- .InCreativeTab(CreativeTab.ToolsAndWeapons));
+ .InCreativeTab(CreativeTab.ToolsAndWeapons)
+ .Prepend());
RubyWandItem = Registry.Item.Register("examplemod:ruby_wand", new RubyWand(),
new ItemProperties()
diff --git a/WeaveLoader.API/Block/BlockProperties.cs b/WeaveLoader.API/Block/BlockProperties.cs
index e8fcae1..5e1c7bc 100644
--- a/WeaveLoader.API/Block/BlockProperties.cs
+++ b/WeaveLoader.API/Block/BlockProperties.cs
@@ -64,6 +64,7 @@ public class BlockProperties
internal float LightEmissionValue = 0.0f;
internal int LightBlockValue = 255;
internal CreativeTab CreativeTabValue = CreativeTab.None;
+ internal CreativePlacement? CreativePlacementValue;
internal Text? NameValue;
internal int RequiredHarvestLevelValue = -1;
internal ToolType RequiredToolValue = ToolType.None;
@@ -82,6 +83,8 @@ public class BlockProperties
public BlockProperties LightBlocking(int level) { LightBlockValue = level; return this; }
public BlockProperties Indestructible() { HardnessValue = -1.0f; ResistanceValue = 6000000f; return this; }
public BlockProperties InCreativeTab(CreativeTab tab) { CreativeTabValue = tab; return this; }
+ public BlockProperties CreativePlacement(CreativePlacement placement) { CreativePlacementValue = placement; return this; }
+ public BlockProperties Prepend() { CreativePlacementValue = global::WeaveLoader.API.CreativePlacement.Prepend(); return this; }
/// Display name shown in-game (e.g. "Ruby Ore"). Used for localization.
public BlockProperties Name(string displayName) { NameValue = Text.Literal(displayName); return this; }
/// Localized display name using a language key (e.g. "block.examplemod.ruby_ore").
diff --git a/WeaveLoader.API/Block/BlockRegistry.cs b/WeaveLoader.API/Block/BlockRegistry.cs
index 1f1af70..035ec67 100644
--- a/WeaveLoader.API/Block/BlockRegistry.cs
+++ b/WeaveLoader.API/Block/BlockRegistry.cs
@@ -65,14 +65,13 @@ public static class BlockRegistry
properties.AcceptsRedstonePowerValue ? 1 : 0);
if (numericId < 0)
- throw new InvalidOperationException($"Failed to register block '{id}'. No free IDs or invalid parameters.");
-
- if (properties.CreativeTabValue != CreativeTab.None)
{
- NativeInterop.native_add_to_creative(numericId, 1, 0, (int)properties.CreativeTabValue);
- Logger.Debug($"Block '{id}' added to creative tab {properties.CreativeTabValue}");
+ Logger.Error($"Failed to register block '{id}'. No free IDs or invalid parameters.");
+ throw new InvalidOperationException($"Failed to register block '{id}'. No free IDs or invalid parameters.");
}
+ AddToCreative(id, numericId, properties);
+
Logger.Debug($"Registered block '{id}' -> numeric ID {numericId}");
lock (s_lock)
{
@@ -104,13 +103,13 @@ public static class BlockRegistry
properties.AcceptsRedstonePowerValue ? 1 : 0);
if (numericId < 0)
- throw new InvalidOperationException($"Failed to register managed block '{id}'.");
-
- if (properties.CreativeTabValue != CreativeTab.None)
{
- NativeInterop.native_add_to_creative(numericId, 1, 0, (int)properties.CreativeTabValue);
+ Logger.Error($"Failed to register managed block '{id}'.");
+ throw new InvalidOperationException($"Failed to register managed block '{id}'.");
}
+ AddToCreative(id, numericId, properties);
+
ManagedBlockDispatcher.RegisterBlock(id, numericId, managedBlock);
int dropNumericId = -1;
@@ -150,13 +149,13 @@ public static class BlockRegistry
properties.AcceptsRedstonePowerValue ? 1 : 0);
if (numericId < 0)
- throw new InvalidOperationException($"Failed to register falling block '{id}'.");
-
- if (properties.CreativeTabValue != CreativeTab.None)
{
- NativeInterop.native_add_to_creative(numericId, 1, 0, (int)properties.CreativeTabValue);
+ Logger.Error($"Failed to register falling block '{id}'.");
+ throw new InvalidOperationException($"Failed to register falling block '{id}'.");
}
+ AddToCreative(id, numericId, properties);
+
if (managedBlock != null)
{
ManagedBlockDispatcher.RegisterBlock(id, numericId, managedBlock);
@@ -198,14 +197,17 @@ public static class BlockRegistry
out int doubleNumericId);
if (numericId < 0)
- throw new InvalidOperationException($"Failed to register slab block '{id}'.");
- if (doubleNumericId < 0)
- throw new InvalidOperationException($"Failed to resolve generated slab pair '{doubleId}'.");
-
- if (properties.CreativeTabValue != CreativeTab.None)
{
- NativeInterop.native_add_to_creative(numericId, 1, 0, (int)properties.CreativeTabValue);
+ Logger.Error($"Failed to register slab block '{id}'.");
+ throw new InvalidOperationException($"Failed to register slab block '{id}'.");
}
+ if (doubleNumericId < 0)
+ {
+ Logger.Error($"Failed to resolve generated slab pair '{doubleId}'.");
+ throw new InvalidOperationException($"Failed to resolve generated slab pair '{doubleId}'.");
+ }
+
+ AddToCreative(id, numericId, properties);
lock (s_lock)
{
@@ -222,4 +224,59 @@ public static class BlockRegistry
return s_idByNumeric.TryGetValue(numericId, out id);
}
}
+
+ private static void AddToCreative(Identifier id, int numericId, BlockProperties properties)
+ {
+ if (properties.CreativeTabValue == CreativeTab.None)
+ {
+ Logger.Debug($"Block '{id}' not added to creative (CreativeTab.None)");
+ return;
+ }
+
+ bool added = false;
+ if (properties.CreativePlacementValue.HasValue)
+ {
+ CreativePlacement placement = properties.CreativePlacementValue.Value;
+ if (placement.Insert == CreativeInsert.Prepend)
+ {
+ try
+ {
+ NativeInterop.native_add_to_creative_ex(
+ numericId, 1, 0, (int)properties.CreativeTabValue,
+ (int)placement.Insert, -1, -1);
+ }
+ catch (DllNotFoundException e)
+ {
+ Logger.Error($"Creative add failed for block '{id}': {e.Message}. Check that WeaveLoaderRuntime is present.");
+ throw;
+ }
+ catch (EntryPointNotFoundException e)
+ {
+ Logger.Error($"Creative add failed for block '{id}': {e.Message}. API/runtime mismatch.");
+ throw;
+ }
+ added = true;
+ }
+ }
+
+ if (!added)
+ {
+ try
+ {
+ NativeInterop.native_add_to_creative(numericId, 1, 0, (int)properties.CreativeTabValue);
+ }
+ catch (DllNotFoundException e)
+ {
+ Logger.Error($"Creative add failed for block '{id}': {e.Message}. Check that WeaveLoaderRuntime is present.");
+ throw;
+ }
+ catch (EntryPointNotFoundException e)
+ {
+ Logger.Error($"Creative add failed for block '{id}': {e.Message}. API/runtime mismatch.");
+ throw;
+ }
+ }
+
+ Logger.Debug($"Block '{id}' added to creative tab {properties.CreativeTabValue}");
+ }
}
diff --git a/WeaveLoader.API/CreativePlacement.cs b/WeaveLoader.API/CreativePlacement.cs
new file mode 100644
index 0000000..033c5e6
--- /dev/null
+++ b/WeaveLoader.API/CreativePlacement.cs
@@ -0,0 +1,20 @@
+namespace WeaveLoader.API;
+
+public enum CreativeInsert
+{
+ Append = 0,
+ Prepend = 1
+}
+
+public readonly struct CreativePlacement
+{
+ public CreativeInsert Insert { get; }
+
+ private CreativePlacement(CreativeInsert insert)
+ {
+ Insert = insert;
+ }
+
+ public static CreativePlacement Append() => new(CreativeInsert.Append);
+ public static CreativePlacement Prepend() => new(CreativeInsert.Prepend);
+}
diff --git a/WeaveLoader.API/Item/ItemProperties.cs b/WeaveLoader.API/Item/ItemProperties.cs
index 8ae34d8..294d41e 100644
--- a/WeaveLoader.API/Item/ItemProperties.cs
+++ b/WeaveLoader.API/Item/ItemProperties.cs
@@ -12,6 +12,7 @@ public class ItemProperties
internal float AttackDamageValue = 0.0f;
internal string IconValue = "";
internal CreativeTab CreativeTabValue = CreativeTab.None;
+ internal CreativePlacement? CreativePlacementValue;
internal Text? NameValue;
public ItemProperties MaxStackSize(int size) { MaxStackSizeValue = size; return this; }
@@ -29,6 +30,8 @@ public class ItemProperties
/// Override the native attack damage value for tool items.
public ItemProperties AttackDamage(float damage) { AttackDamageValue = damage; return this; }
public ItemProperties InCreativeTab(CreativeTab tab) { CreativeTabValue = tab; return this; }
+ public ItemProperties CreativePlacement(CreativePlacement placement) { CreativePlacementValue = placement; return this; }
+ public ItemProperties Prepend() { CreativePlacementValue = global::WeaveLoader.API.CreativePlacement.Prepend(); return this; }
/// Display name shown in-game (e.g. "Ruby"). Used for localization.
public ItemProperties Name(string displayName) { NameValue = Text.Literal(displayName); return this; }
/// Localized display name using a language key (e.g. "item.examplemod.ruby").
diff --git a/WeaveLoader.API/Item/ItemRegistry.cs b/WeaveLoader.API/Item/ItemRegistry.cs
index 80a5b30..fca7189 100644
--- a/WeaveLoader.API/Item/ItemRegistry.cs
+++ b/WeaveLoader.API/Item/ItemRegistry.cs
@@ -184,13 +184,63 @@ public static class ItemRegistry
}
if (numericId < 0)
+ {
+ Logger.Error($"Failed to register item '{id}'. No free IDs or invalid parameters.");
throw new InvalidOperationException($"Failed to register item '{id}'. No free IDs or invalid parameters.");
+ }
if (properties.CreativeTabValue != CreativeTab.None)
{
- NativeInterop.native_add_to_creative(numericId, 1, 0, (int)properties.CreativeTabValue);
+ bool added = false;
+ if (properties.CreativePlacementValue.HasValue)
+ {
+ CreativePlacement placement = properties.CreativePlacementValue.Value;
+ if (placement.Insert == CreativeInsert.Prepend)
+ {
+ try
+ {
+ NativeInterop.native_add_to_creative_ex(
+ numericId, 1, 0, (int)properties.CreativeTabValue,
+ (int)placement.Insert, -1, -1);
+ }
+ catch (DllNotFoundException e)
+ {
+ Logger.Error($"Creative add failed for item '{id}': {e.Message}. Check that WeaveLoaderRuntime is present.");
+ throw;
+ }
+ catch (EntryPointNotFoundException e)
+ {
+ Logger.Error($"Creative add failed for item '{id}': {e.Message}. API/runtime mismatch.");
+ throw;
+ }
+ added = true;
+ }
+ }
+
+ if (!added)
+ {
+ try
+ {
+ NativeInterop.native_add_to_creative(numericId, 1, 0, (int)properties.CreativeTabValue);
+ }
+ catch (DllNotFoundException e)
+ {
+ Logger.Error($"Creative add failed for item '{id}': {e.Message}. Check that WeaveLoaderRuntime is present.");
+ throw;
+ }
+ catch (EntryPointNotFoundException e)
+ {
+ Logger.Error($"Creative add failed for item '{id}': {e.Message}. API/runtime mismatch.");
+ throw;
+ }
+ }
+
Logger.Debug($"Item '{id}' added to creative tab {properties.CreativeTabValue}");
}
+ else
+ {
+ Logger.Debug($"Item '{id}' not added to creative (CreativeTab.None)");
+ }
if (managedItem != null)
{
diff --git a/WeaveLoader.API/NativeInterop.cs b/WeaveLoader.API/NativeInterop.cs
index 3f0c190..15e3534 100644
--- a/WeaveLoader.API/NativeInterop.cs
+++ b/WeaveLoader.API/NativeInterop.cs
@@ -225,6 +225,9 @@ internal static class NativeInterop
[DllImport(RuntimeDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern void native_add_to_creative(int numericId, int count, int auxValue, int groupIndex);
+ [DllImport(RuntimeDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void native_add_to_creative_ex(int numericId, int count, int auxValue, int groupIndex, int insertMode, int anchorId, int anchorAux);
+
[DllImport(RuntimeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern nint native_find_symbol(string fullName);
diff --git a/WeaveLoaderRuntime/src/CreativeInventory.cpp b/WeaveLoaderRuntime/src/CreativeInventory.cpp
index 89143b2..fa5f1b8 100644
--- a/WeaveLoaderRuntime/src/CreativeInventory.cpp
+++ b/WeaveLoaderRuntime/src/CreativeInventory.cpp
@@ -16,6 +16,7 @@ void* pSpecs = nullptr;
std::vector s_pendingItems;
static bool s_didInjectItems = false;
+static bool s_creativeReady = false;
static const int CREATIVE_GROUP_COUNT = 15;
static const int SIZEOF_MSVC_VECTOR = 24;
@@ -49,8 +50,27 @@ typedef void (__fastcall *VectorPushBackMove_fn)(void* vectorThis, void* sharedP
void AddPending(int itemId, int count, int auxValue, int groupIndex)
{
- s_pendingItems.push_back({ itemId, count, auxValue, groupIndex });
- LogUtil::Log("[WeaveLoader] Queued creative item: id=%d group=%d", itemId, groupIndex);
+ AddPendingEx(itemId, count, auxValue, groupIndex, InsertAppend, -1, -1);
+}
+
+void AddPendingEx(int itemId, int count, int auxValue, int groupIndex, int insertMode, int anchorId, int anchorAux)
+{
+ (void)anchorId;
+ (void)anchorAux;
+ s_pendingItems.push_back({ itemId, count, auxValue, groupIndex, insertMode });
+ LogUtil::Log("[WeaveLoader] Queued creative item: id=%d group=%d mode=%d",
+ itemId, groupIndex, insertMode);
+
+ if (s_creativeReady)
+ {
+ InjectItems();
+ UpdateTabPageCounts();
+ }
+}
+
+void SetCreativeReady()
+{
+ s_creativeReady = true;
}
bool ResolveSymbols(SymbolResolver& resolver)
@@ -90,7 +110,7 @@ bool ResolveSymbols(SymbolResolver& resolver)
PdbParser::DumpMatching("specs@IUIScene_CreativeMenu");
}
- return pCategoryGroups && pItemInstanceCtor && pSharedPtrCtor && pVectorPushBack;
+ return pCategoryGroups && pItemInstanceCtor && pSharedPtrCtor;
}
static size_t ReadVectorSize(char* vec)
@@ -101,6 +121,102 @@ static size_t ReadVectorSize(char* vec)
return static_cast((last - first) / SIZEOF_MSVC_SHARED_PTR);
}
+static char* VectorBegin(char* vec) { return *reinterpret_cast(vec); }
+static char* VectorEnd(char* vec) { return *reinterpret_cast(vec + 8); }
+static char* VectorCap(char* vec) { return *reinterpret_cast(vec + 16); }
+
+static void SetVectorPointers(char* vec, char* begin, char* end, char* cap)
+{
+ *reinterpret_cast(vec) = begin;
+ *reinterpret_cast(vec + 8) = end;
+ *reinterpret_cast(vec + 16) = cap;
+}
+
+static bool EnsureVectorCapacity(char* vec, size_t desiredCount)
+{
+ char* begin = VectorBegin(vec);
+ char* end = VectorEnd(vec);
+ char* cap = VectorCap(vec);
+ size_t size = begin ? static_cast((end - begin) / SIZEOF_MSVC_SHARED_PTR) : 0;
+ size_t capacity = begin ? static_cast((cap - begin) / SIZEOF_MSVC_SHARED_PTR) : 0;
+
+ if (capacity >= desiredCount)
+ return true;
+
+ size_t newCap = capacity ? capacity * 2 : 4;
+ if (newCap < desiredCount)
+ newCap = desiredCount;
+
+ char* newBuf = reinterpret_cast(::operator new(newCap * SIZEOF_MSVC_SHARED_PTR));
+ if (!newBuf)
+ return false;
+
+ if (begin && size > 0)
+ memcpy(newBuf, begin, size * SIZEOF_MSVC_SHARED_PTR);
+
+ if (begin)
+ ::operator delete(begin);
+
+ SetVectorPointers(vec, newBuf, newBuf + size * SIZEOF_MSVC_SHARED_PTR, newBuf + newCap * SIZEOF_MSVC_SHARED_PTR);
+ return true;
+}
+
+static bool InsertSharedPtrAt(char* vec, const void* sharedPtrBuf, size_t index)
+{
+ size_t size = ReadVectorSize(vec);
+ if (index > size) index = size;
+
+ if (!EnsureVectorCapacity(vec, size + 1))
+ return false;
+
+ char* begin = VectorBegin(vec);
+ char* end = VectorEnd(vec);
+ char* insertPos = begin + index * SIZEOF_MSVC_SHARED_PTR;
+ size_t tailBytes = static_cast(end - insertPos);
+ memmove(insertPos + SIZEOF_MSVC_SHARED_PTR, insertPos, tailBytes);
+ memcpy(insertPos, sharedPtrBuf, SIZEOF_MSVC_SHARED_PTR);
+ SetVectorPointers(vec, begin, end + SIZEOF_MSVC_SHARED_PTR, VectorCap(vec));
+ return true;
+}
+
+static bool InsertItemInstance(char* vec, const PendingCreativeItem& item, size_t insertIndex,
+ ItemInstanceCtor_fn ctorFn, SharedPtrCtor_fn spCtorFn)
+{
+ size_t sizeBefore = ReadVectorSize(vec);
+
+ void* rawItem = ::operator new(ITEMINSTANCE_ALLOC_SIZE);
+ memset(rawItem, 0, ITEMINSTANCE_ALLOC_SIZE);
+ ctorFn(rawItem, item.itemId, item.count, item.auxValue);
+
+ // Verify ItemInstance vtable was set (first 8 bytes should be non-null)
+ void* vtable = *reinterpret_cast(rawItem);
+ LogUtil::Log("[WeaveLoader] ItemInstance(%d,%d,%d) @ %p, vtable=%p",
+ item.itemId, item.count, item.auxValue, rawItem, vtable);
+
+ char spBuf[16];
+ memset(spBuf, 0, sizeof(spBuf));
+ spCtorFn(spBuf, rawItem);
+
+ // Log shared_ptr contents (ptr + control block)
+ void* spPtr = *reinterpret_cast(spBuf);
+ void* spCtrl = *reinterpret_cast(spBuf + 8);
+ LogUtil::Log("[WeaveLoader] shared_ptr: ptr=%p ctrl=%p", spPtr, spCtrl);
+
+ bool inserted = InsertSharedPtrAt(vec, spBuf, insertIndex);
+ size_t sizeAfter = ReadVectorSize(vec);
+ if (!inserted)
+ {
+ LogUtil::Log("[WeaveLoader] Failed to insert creative item id=%d into group %d",
+ item.itemId, item.groupIndex);
+ return false;
+ }
+
+ LogUtil::Log("[WeaveLoader] Injected item id=%d into creative group %d "
+ "(vector @ %p, size: %zu -> %zu, index=%zu)",
+ item.itemId, item.groupIndex, vec, sizeBefore, sizeAfter, insertIndex);
+ return true;
+}
+
void UpdateTabPageCounts()
{
if (!s_didInjectItems)
@@ -163,7 +279,7 @@ void UpdateTabPageCounts()
void InjectItems()
{
- if (!pCategoryGroups || !pItemInstanceCtor || !pSharedPtrCtor || !pVectorPushBack)
+ if (!pCategoryGroups || !pItemInstanceCtor || !pSharedPtrCtor)
{
LogUtil::Log("[WeaveLoader] Cannot inject creative items: missing symbols");
return;
@@ -171,20 +287,21 @@ void InjectItems()
if (s_pendingItems.empty())
{
- LogUtil::Log("[WeaveLoader] No creative items to inject");
- s_didInjectItems = false;
+ if (s_didInjectItems)
+ LogUtil::Log("[WeaveLoader] No creative items to inject (pending empty; already injected)");
+ else
+ LogUtil::Log("[WeaveLoader] No creative items to inject (none queued)");
return;
}
s_didInjectItems = true;
auto ctorFn = reinterpret_cast(pItemInstanceCtor);
auto spCtorFn = reinterpret_cast(pSharedPtrCtor);
- auto pushFn = reinterpret_cast(pVectorPushBack);
-
// categoryGroups is a static array of vectors (vector<...> categoryGroups[15]).
// pCategoryGroups is the address of the first vector; no dereference needed.
char* groups = reinterpret_cast(pCategoryGroups);
+ std::vector grouped[CREATIVE_GROUP_COUNT];
for (auto& item : s_pendingItems)
{
if (item.groupIndex < 0 || item.groupIndex >= CREATIVE_GROUP_COUNT)
@@ -193,37 +310,36 @@ void InjectItems()
item.itemId, item.groupIndex);
continue;
}
-
- void* rawItem = ::operator new(ITEMINSTANCE_ALLOC_SIZE);
- memset(rawItem, 0, ITEMINSTANCE_ALLOC_SIZE);
- ctorFn(rawItem, item.itemId, item.count, item.auxValue);
-
- // Verify ItemInstance vtable was set (first 8 bytes should be non-null)
- void* vtable = *reinterpret_cast(rawItem);
- LogUtil::Log("[WeaveLoader] ItemInstance(%d,%d,%d) @ %p, vtable=%p",
- item.itemId, item.count, item.auxValue, rawItem, vtable);
-
- char spBuf[16];
- memset(spBuf, 0, sizeof(spBuf));
- spCtorFn(spBuf, rawItem);
-
- // Log shared_ptr contents (ptr + control block)
- void* spPtr = *reinterpret_cast(spBuf);
- void* spCtrl = *reinterpret_cast(spBuf + 8);
- LogUtil::Log("[WeaveLoader] shared_ptr: ptr=%p ctrl=%p", spPtr, spCtrl);
-
- char* vec = groups + item.groupIndex * SIZEOF_MSVC_VECTOR;
- size_t sizeBefore = ReadVectorSize(vec);
-
- pushFn(vec, spBuf);
-
- size_t sizeAfter = ReadVectorSize(vec);
- LogUtil::Log("[WeaveLoader] Injected item id=%d into creative group %d "
- "(vector @ %p, size: %zu -> %zu)",
- item.itemId, item.groupIndex, vec, sizeBefore, sizeAfter);
+ grouped[item.groupIndex].push_back(item);
}
- LogUtil::Log("[WeaveLoader] Injected %zu items into creative inventory", s_pendingItems.size());
+ size_t injectedCount = 0;
+ for (int groupIndex = 0; groupIndex < CREATIVE_GROUP_COUNT; ++groupIndex)
+ {
+ auto& pending = grouped[groupIndex];
+ if (pending.empty())
+ continue;
+
+ char* vec = groups + groupIndex * SIZEOF_MSVC_VECTOR;
+ size_t prependIndex = 0;
+ for (auto& item : pending)
+ {
+ size_t insertIndex = ReadVectorSize(vec);
+ if (item.insertMode == InsertPrepend)
+ {
+ insertIndex = prependIndex;
+ ++prependIndex;
+ }
+
+ if (InsertItemInstance(vec, item, insertIndex, ctorFn, spCtorFn))
+ ++injectedCount;
+ else
+ LogUtil::Log("[WeaveLoader] Failed to insert creative item id=%d in group %d",
+ item.itemId, groupIndex);
+ }
+ }
+
+ LogUtil::Log("[WeaveLoader] Injected %zu items into creative inventory", injectedCount);
s_pendingItems.clear();
}
diff --git a/WeaveLoaderRuntime/src/CreativeInventory.h b/WeaveLoaderRuntime/src/CreativeInventory.h
index 190a70b..bfadfea 100644
--- a/WeaveLoaderRuntime/src/CreativeInventory.h
+++ b/WeaveLoaderRuntime/src/CreativeInventory.h
@@ -9,11 +9,22 @@ struct PendingCreativeItem
int count;
int auxValue;
int groupIndex;
+ int insertMode;
};
namespace CreativeInventory
{
+ enum InsertMode
+ {
+ InsertAppend = 0,
+ InsertPrepend = 1,
+ InsertBefore = 2,
+ InsertAfter = 3
+ };
+
void AddPending(int itemId, int count, int auxValue, int groupIndex);
+ void AddPendingEx(int itemId, int count, int auxValue, int groupIndex, int insertMode, int anchorId, int anchorAux);
+ void SetCreativeReady();
bool ResolveSymbols(SymbolResolver& resolver);
void InjectItems();
void UpdateTabPageCounts();
diff --git a/WeaveLoaderRuntime/src/GameHooks.cpp b/WeaveLoaderRuntime/src/GameHooks.cpp
index 330a23a..e4cb096 100644
--- a/WeaveLoaderRuntime/src/GameHooks.cpp
+++ b/WeaveLoaderRuntime/src/GameHooks.cpp
@@ -2637,6 +2637,7 @@ namespace GameHooks
{
LogUtil::Log("[WeaveLoader] Hook: CreativeStaticCtor -- building vanilla creative lists");
Original_CreativeStaticCtor();
+ CreativeInventory::SetCreativeReady();
// Inject AFTER vanilla lists so modded entries are appended to the end
// of each creative category.
diff --git a/WeaveLoaderRuntime/src/NativeExports.cpp b/WeaveLoaderRuntime/src/NativeExports.cpp
index cd2e3f3..26526bc 100644
--- a/WeaveLoaderRuntime/src/NativeExports.cpp
+++ b/WeaveLoaderRuntime/src/NativeExports.cpp
@@ -772,6 +772,11 @@ void native_add_to_creative(int numericId, int count, int auxValue, int groupInd
CreativeInventory::AddPending(numericId, count, auxValue, groupIndex);
}
+void native_add_to_creative_ex(int numericId, int count, int auxValue, int groupIndex, int insertMode, int anchorId, int anchorAux)
+{
+ CreativeInventory::AddPendingEx(numericId, count, auxValue, groupIndex, insertMode, anchorId, anchorAux);
+}
+
void* native_find_symbol(const char* fullName)
{
return SymbolRegistry::Instance().FindAddress(fullName);
diff --git a/WeaveLoaderRuntime/src/NativeExports.h b/WeaveLoaderRuntime/src/NativeExports.h
index f194628..c144c9b 100644
--- a/WeaveLoaderRuntime/src/NativeExports.h
+++ b/WeaveLoaderRuntime/src/NativeExports.h
@@ -193,6 +193,14 @@ extern "C"
int count,
int auxValue,
int groupIndex);
+ __declspec(dllexport) void native_add_to_creative_ex(
+ int numericId,
+ int count,
+ int auxValue,
+ int groupIndex,
+ int insertMode,
+ int anchorId,
+ int anchorAux);
__declspec(dllexport) void* native_find_symbol(const char* fullName);
__declspec(dllexport) int native_has_symbol(const char* fullName);