I was considering writing a plugin that could bind any command to any cItem in the player's inventory, and execute that command whenever the player clicked with that tool in hand. Such a plugin would come in handy especially on the Gallery server, when applying the same command to multiple areas.
I found out about some interesting behaviors that I'd like to describe, for documentation purposes.
My first attempt was to "hide" the command into cItem.m_Lore, somehow tricking the client into not displaying it but still being accessible to the plugin. For this, I tried embedding a NUL character into the string. Unfortunately, this seems to trim the string in the API bindings code, so the hidden part never really came back. While experimenting with m_Lore more, I learned about a few interesting things: when multiple successive line-breaks are given, the parser removes all but one. When using non-printable characters (in the 1 - 31 range), the client would still print them as little boxes with abbreviations. When using a character in the range 128 - 255, the client would disconnect and is no longer able to connect, always failing with "unable to parse NBT" exception. I assume that is due to the string being invalid UTF-8.
My second attempt was to use the cItem.m_CustomName with the same trick of hiding the data. Again, this didn't succeed, the additional data disappeared on the API boundary. And again, the client would print the nonprintable characters and disconnect with invalid UTF-8.
The third attempt was an elaborat Lua trick: store a custom value directly in the cItem object that represents the item in Lua - since it is a table, it can store any value after all. This does seem to work, but has a terrible short-coming: The cItem returned by cInventory:GetEquippedItem() represents the slot, rather than the item in it. So when the player moves another item into the slot, it still retains the custom value, and the item moved out of the slot "loses' the custom value. Therefore, this approach is unusable for the purpose of this plugin as well.
In the end, I seem to be left with three solutions:
- A plugin can keep track of the item manually, by storing the item's current location and adjusting it whenever there's a UI click by the player. Quite elaborate, and prone to errors.
- Add a value storage in the C++ code and export it to Lua. This is the cleanest solution, but somewhat incompatible - there's no such storage in Vanilla and who knows what the client could do if it tried to read the Anvil world saved by Cuberite with this "feature" used.
- Store the data in m_Lore and don't care about the player seeing it. Ugly, but works.
Or can you see some other option that I forgot?
I found out about some interesting behaviors that I'd like to describe, for documentation purposes.
My first attempt was to "hide" the command into cItem.m_Lore, somehow tricking the client into not displaying it but still being accessible to the plugin. For this, I tried embedding a NUL character into the string. Unfortunately, this seems to trim the string in the API bindings code, so the hidden part never really came back. While experimenting with m_Lore more, I learned about a few interesting things: when multiple successive line-breaks are given, the parser removes all but one. When using non-printable characters (in the 1 - 31 range), the client would still print them as little boxes with abbreviations. When using a character in the range 128 - 255, the client would disconnect and is no longer able to connect, always failing with "unable to parse NBT" exception. I assume that is due to the string being invalid UTF-8.
My second attempt was to use the cItem.m_CustomName with the same trick of hiding the data. Again, this didn't succeed, the additional data disappeared on the API boundary. And again, the client would print the nonprintable characters and disconnect with invalid UTF-8.
The third attempt was an elaborat Lua trick: store a custom value directly in the cItem object that represents the item in Lua - since it is a table, it can store any value after all. This does seem to work, but has a terrible short-coming: The cItem returned by cInventory:GetEquippedItem() represents the slot, rather than the item in it. So when the player moves another item into the slot, it still retains the custom value, and the item moved out of the slot "loses' the custom value. Therefore, this approach is unusable for the purpose of this plugin as well.
In the end, I seem to be left with three solutions:
- A plugin can keep track of the item manually, by storing the item's current location and adjusting it whenever there's a UI click by the player. Quite elaborate, and prone to errors.
- Add a value storage in the C++ code and export it to Lua. This is the cleanest solution, but somewhat incompatible - there's no such storage in Vanilla and who knows what the client could do if it tried to read the Anvil world saved by Cuberite with this "feature" used.
- Store the data in m_Lore and don't care about the player seeing it. Ugly, but works.
Or can you see some other option that I forgot?