mirror of
https://github.com/Jacobwasbeast/LegacyWeaveLoader.git
synced 2026-05-29 17:14:33 +00:00
refactor(creative): prepend support and logging
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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; }
|
||||
/// <summary>Display name shown in-game (e.g. "Ruby Ore"). Used for localization.</summary>
|
||||
public BlockProperties Name(string displayName) { NameValue = Text.Literal(displayName); return this; }
|
||||
/// <summary>Localized display name using a language key (e.g. "block.examplemod.ruby_ore").</summary>
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
20
WeaveLoader.API/CreativePlacement.cs
Normal file
20
WeaveLoader.API/CreativePlacement.cs
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
/// <summary>Override the native attack damage value for tool items.</summary>
|
||||
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; }
|
||||
/// <summary>Display name shown in-game (e.g. "Ruby"). Used for localization.</summary>
|
||||
public ItemProperties Name(string displayName) { NameValue = Text.Literal(displayName); return this; }
|
||||
/// <summary>Localized display name using a language key (e.g. "item.examplemod.ruby").</summary>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ void* pSpecs = nullptr;
|
||||
|
||||
std::vector<PendingCreativeItem> 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<size_t>((last - first) / SIZEOF_MSVC_SHARED_PTR);
|
||||
}
|
||||
|
||||
static char* VectorBegin(char* vec) { return *reinterpret_cast<char**>(vec); }
|
||||
static char* VectorEnd(char* vec) { return *reinterpret_cast<char**>(vec + 8); }
|
||||
static char* VectorCap(char* vec) { return *reinterpret_cast<char**>(vec + 16); }
|
||||
|
||||
static void SetVectorPointers(char* vec, char* begin, char* end, char* cap)
|
||||
{
|
||||
*reinterpret_cast<char**>(vec) = begin;
|
||||
*reinterpret_cast<char**>(vec + 8) = end;
|
||||
*reinterpret_cast<char**>(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<size_t>((end - begin) / SIZEOF_MSVC_SHARED_PTR) : 0;
|
||||
size_t capacity = begin ? static_cast<size_t>((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<char*>(::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<size_t>(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<void**>(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<void**>(spBuf);
|
||||
void* spCtrl = *reinterpret_cast<void**>(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<ItemInstanceCtor_fn>(pItemInstanceCtor);
|
||||
auto spCtorFn = reinterpret_cast<SharedPtrCtor_fn>(pSharedPtrCtor);
|
||||
auto pushFn = reinterpret_cast<VectorPushBackMove_fn>(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<char*>(pCategoryGroups);
|
||||
|
||||
std::vector<PendingCreativeItem> 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<void**>(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<void**>(spBuf);
|
||||
void* spCtrl = *reinterpret_cast<void**>(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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user