mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/LCE-Revelations.git
synced 2026-05-22 08:05:48 +00:00
Goal: Allow players to type and display text in any language supported by Unicode, including Chinese, Japanese, Korean, Thai, Arabic, Korean, Hindi, and more. This covers all text surfaces: chat editor, chat messages, signs (in-world and editor), world name/seed, server address/port fields, and all Iggy Flash UI text fields. Multi-language support: Two complementary rendering systems were added to handle Unicode text across the entire client: 1. Iggy UI (Flash-based text fields): A new UIUnicodeBitmapFont class serves Java Minecraft's glyph page PNGs (glyph_00.png-glyph_FF.png) through Iggy's bitmap font provider API. Registered as the global fallback font with metrics matching the Mojangles bitmap font for correct baseline alignment. When the primary bitmap font lacks a glyph, it returns IGGY_GLYPH_INVALID and Iggy seamlessly falls back to the unicode bitmap font. 2. Legacy C++ Font renderer (chat editor, in-world signs): Revived the commented-out unicode glyph page system in Font.cpp. Characters not in the bitmap font texture are rendered from glyph page PNGs loaded on demand, with proper texture switching mid-string. 3. ChatScreen input: Removed the restrictive acceptableLetters filter so all printable Unicode characters are accepted in chat. Languages now supported for text input and rendering: - Japanese (Hiragana, Katakana, Kanji) - Chinese (Simplified and Traditional) - Korean (Hangul) - Thai - Arabic - Hindi (Devanagari) - Russian (Cyrillic) - already worked via bitmap font - Greek - already worked via bitmap font - Polish, Czech, Turkish (Extended Latin) - already worked via bitmap font - Armenian, Georgian, and other scripts covered by glyph pages Security fixes: - Fixed memset under-initialization of Font::charWidths (zeroed 460 bytes instead of 460*sizeof(int)=1840 bytes, leaving entries 115+ uninitialized) - pre-existing bug - Added bounds checks to all UIUnicodeBitmapFont callbacks to reject glyph IDs outside [0, 65535], preventing OOB array access - Added bounds check in Font::width() section-sign fallback path to prevent OOB read on charWidths[] with high codepoints - Blocked Unicode bidirectional override characters (U+202A-202E, U+2066-2069) in chat input to prevent message spoofing Memory leak fix: - Fixed SignTileEntity::load allocating wchar_t[256] with new[] on every sign load without freeing. Replaced with stack allocation. Debug logging: - Added [SIGN] prefixed logging for sign save/update operations - Added [CHAT] prefixed logging for chat send/receive operations Files changed: - UIUnicodeBitmapFont.h/.cpp (new) - Iggy bitmap font for glyph pages - UIBitmapFont.cpp - Return IGGY_GLYPH_INVALID for unknown chars - UIFontData.h/.cpp - Added hasGlyph() method - UIController.h/.cpp - Load and register unicode bitmap fallback font - UITTFFont.h/.cpp - Added registerAsDefaultFonts parameter - Font.h/.cpp - Revived unicode glyph page rendering system - ChatScreen.cpp - Accept all Unicode input, block bidi overrides - Gui.cpp - Chat display debug logging - ClientConnection.cpp - Sign update debug logging - SignTileEntity.cpp - Sign save logging, memory leak fix
219 lines
5.5 KiB
C++
219 lines
5.5 KiB
C++
#include "stdafx.h"
|
|
#include "com.mojang.nbt.h"
|
|
#include "net.minecraft.world.level.h"
|
|
#include "net.minecraft.world.entity.item.h"
|
|
#include "net.minecraft.world.entity.player.h"
|
|
#include "net.minecraft.world.item.h"
|
|
#include "net.minecraft.world.entity.h"
|
|
#include "net.minecraft.world.phys.h"
|
|
#include "net.minecraft.network.packet.h"
|
|
#include "SignTileEntity.h"
|
|
#include <xuiapp.h>
|
|
#include "..\Minecraft.Client\ClientConnection.h"
|
|
#include "..\Minecraft.Client\Minecraft.h"
|
|
#include "..\Minecraft.Client\ServerLevel.h"
|
|
#include "..\Minecraft.World\Level.h"
|
|
|
|
|
|
|
|
const int SignTileEntity::MAX_LINE_LENGTH = 15;
|
|
|
|
SignTileEntity::SignTileEntity() : TileEntity()
|
|
{
|
|
m_wsmessages[0] = L"";
|
|
m_wsmessages[1] = L"";
|
|
m_wsmessages[2] = L"";
|
|
m_wsmessages[3] = L"";
|
|
m_bVerified=true;
|
|
m_bCensored=false;
|
|
|
|
m_iSelectedLine = -1;
|
|
|
|
_isEditable = true;
|
|
|
|
playerWhoMayEdit = nullptr;
|
|
}
|
|
|
|
SignTileEntity::~SignTileEntity()
|
|
{
|
|
// TODO ORBIS_STUBBED;
|
|
#ifndef __ORBIS__
|
|
// 4J-PB - we don't need to verify strings anymore - InputManager.CancelQueuedVerifyStrings(&SignTileEntity::StringVerifyCallback,(LPVOID)this);
|
|
#endif
|
|
}
|
|
|
|
void SignTileEntity::save(CompoundTag *tag)
|
|
{
|
|
TileEntity::save(tag);
|
|
tag->putString(L"Text1", m_wsmessages[0] );
|
|
tag->putString(L"Text2", m_wsmessages[1] );
|
|
tag->putString(L"Text3", m_wsmessages[2] );
|
|
tag->putString(L"Text4", m_wsmessages[3] );
|
|
#ifndef _CONTENT_PACKAGE
|
|
app.DebugPrintf("[SIGN] Saving sign at (%d, %d, %d):\n", x, y, z);
|
|
for(int i=0;i<4;i++)
|
|
{
|
|
app.DebugPrintf("[SIGN] Line%d: \"%ls\"\n", i+1, m_wsmessages[i].c_str());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void SignTileEntity::load(CompoundTag *tag)
|
|
{
|
|
_isEditable = false;
|
|
TileEntity::load(tag);
|
|
for (int i = 0; i < MAX_SIGN_LINES; i++)
|
|
{
|
|
wchar_t buf[16];
|
|
swprintf(buf, 16, L"Text%d", (i+1) );
|
|
m_wsmessages[i] = tag->getString( buf );
|
|
if (m_wsmessages[i].length() > MAX_LINE_LENGTH) m_wsmessages[i] = m_wsmessages[i].substr(0, MAX_LINE_LENGTH);
|
|
}
|
|
#ifndef _CONTENT_PACKAGE
|
|
OutputDebugStringW(L"### - Loaded a sign with text - \n");
|
|
for(int i=0;i<4;i++)
|
|
{
|
|
OutputDebugStringW(m_wsmessages[i].c_str());
|
|
OutputDebugStringW(L"\n");
|
|
}
|
|
#endif
|
|
|
|
// 4J Stu - Fix for #13531 - Bug: Signs do not Censor after loading a save
|
|
// Set verified as false so that it can be re-verified
|
|
m_bVerified=false;
|
|
|
|
setChanged();
|
|
}
|
|
|
|
shared_ptr<Packet> SignTileEntity::getUpdatePacket()
|
|
{
|
|
wstring copy[MAX_SIGN_LINES];
|
|
for (int i = 0; i < MAX_SIGN_LINES; i++)
|
|
{
|
|
copy[i] = m_wsmessages[i];
|
|
}
|
|
return std::make_shared<SignUpdatePacket>(x, y, z, m_bVerified, m_bCensored, copy);
|
|
}
|
|
|
|
bool SignTileEntity::isEditable()
|
|
{
|
|
return _isEditable;
|
|
}
|
|
|
|
void SignTileEntity::setEditable(bool isEditable)
|
|
{
|
|
this->_isEditable = isEditable;
|
|
if (!isEditable)
|
|
{
|
|
playerWhoMayEdit = nullptr;
|
|
}
|
|
}
|
|
|
|
void SignTileEntity::setAllowedPlayerEditor(shared_ptr<Player> player)
|
|
{
|
|
playerWhoMayEdit = player;
|
|
}
|
|
|
|
shared_ptr<Player> SignTileEntity::getPlayerWhoMayEdit()
|
|
{
|
|
return playerWhoMayEdit;
|
|
}
|
|
|
|
void SignTileEntity::setChanged()
|
|
{
|
|
Minecraft *pMinecraft=Minecraft::GetInstance();
|
|
|
|
// 4J-PB - For TU14 we are allowed to not verify strings anymore !
|
|
m_bVerified=true;
|
|
/*
|
|
if(!g_NetworkManager.IsLocalGame() && !m_bVerified)
|
|
//if (pMinecraft->level->isClientSide)
|
|
{
|
|
WCHAR *wcMessages[MAX_SIGN_LINES];
|
|
for (int i = 0; i < MAX_SIGN_LINES; ++i)
|
|
{
|
|
wcMessages[i]=new WCHAR [MAX_LINE_LENGTH+1];
|
|
ZeroMemory(wcMessages[i],sizeof(WCHAR)*(MAX_LINE_LENGTH+1));
|
|
if(m_wsmessages[i].length()>0)
|
|
{
|
|
memcpy(wcMessages[i],m_wsmessages[i].c_str(),m_wsmessages[i].length()*sizeof(WCHAR));
|
|
}
|
|
}
|
|
// at this point, we can ask the online string verifier if our sign text is ok
|
|
#ifdef __ORBIS__
|
|
m_bVerified=true;
|
|
#else
|
|
|
|
if(!InputManager.VerifyStrings((WCHAR**)&wcMessages,MAX_SIGN_LINES,&SignTileEntity::StringVerifyCallback,(LPVOID)this))
|
|
{
|
|
// Nothing to verify
|
|
m_bVerified=true;
|
|
}
|
|
for(unsigned int i = 0; i < MAX_SIGN_LINES; ++i)
|
|
{
|
|
delete [] wcMessages[i];
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// set the sign to allowed (local game)
|
|
m_bVerified=true;
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
void SignTileEntity::SetMessage(int iIndex,wstring &wsText)
|
|
{
|
|
if (wsText.length() > MAX_LINE_LENGTH) // MAX_LINE_LENGTH == 15
|
|
{
|
|
wsText = wsText.substr(0, MAX_LINE_LENGTH);
|
|
#ifdef _DEBUG
|
|
OutputDebugStringW(L"Sign text truncated to 15 characters\n");
|
|
#endif
|
|
}
|
|
m_wsmessages[iIndex]=wsText;
|
|
}
|
|
|
|
// 4J-PB - added for string verification
|
|
int SignTileEntity::StringVerifyCallback(LPVOID lpParam,STRING_VERIFY_RESPONSE *pResults)
|
|
{
|
|
// results will be in m_pStringVerifyResponse
|
|
SignTileEntity *pClass=static_cast<SignTileEntity *>(lpParam);
|
|
|
|
pClass->m_bVerified=true;
|
|
pClass->m_bCensored=false;
|
|
for(int i=0;i<pResults->wNumStrings;i++)
|
|
{
|
|
if(pResults->pStringResult[i]!=ERROR_SUCCESS)
|
|
{
|
|
pClass->m_bCensored=true;
|
|
}
|
|
}
|
|
|
|
if(!pClass->level->isClientSide)
|
|
{
|
|
ServerLevel *serverLevel = static_cast<ServerLevel *>(pClass->level);
|
|
// 4J Stu - This callback gets called on the main thread, but tried to access things on the server thread. Change to go through the protected method.
|
|
//pClass->level->sendTileUpdated(pClass->x, pClass->y, pClass->z);
|
|
serverLevel->queueSendTileUpdate(pClass->x, pClass->y, pClass->z);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// 4J Added
|
|
shared_ptr<TileEntity> SignTileEntity::clone()
|
|
{
|
|
shared_ptr<SignTileEntity> result = std::make_shared<SignTileEntity>();
|
|
TileEntity::clone(result);
|
|
|
|
result->m_wsmessages[0] = m_wsmessages[0];
|
|
result->m_wsmessages[1] = m_wsmessages[1];
|
|
result->m_wsmessages[2] = m_wsmessages[2];
|
|
result->m_wsmessages[3] = m_wsmessages[3];
|
|
result->m_bVerified = m_bVerified;
|
|
result->m_bCensored = m_bCensored;
|
|
return result;
|
|
} |