Add Chat / Pastes / Formatting (#682)

* Initial fixes for ContainerSetSlotPacket and CraftItemPacket

* Chat: paste, history, § formatting, 1-9 block when open (Windows64)

Made-with: Cursor

* static_cast refactor
This commit is contained in:
Kevin
2026-03-06 09:52:28 -06:00
committed by GitHub
parent 1755cd58be
commit ea65542c1b
16 changed files with 456 additions and 150 deletions

View File

@@ -1,14 +1,27 @@
#include "stdafx.h"
#include "ChatScreen.h"
#include "ClientConnection.h"
#include "Font.h"
#include "MultiplayerLocalPlayer.h"
#include "..\Minecraft.World\SharedConstants.h"
#include "..\Minecraft.World\StringHelpers.h"
#include "..\Minecraft.World\ChatPacket.h"
const wstring ChatScreen::allowedChars = SharedConstants::acceptableLetters;
vector<wstring> ChatScreen::s_chatHistory;
int ChatScreen::s_historyIndex = -1;
wstring ChatScreen::s_historyDraft;
bool ChatScreen::isAllowedChatChar(wchar_t c)
{
return c >= 0x20 && (c == L'\u00A7' || allowedChars.empty() || allowedChars.find(c) != wstring::npos);
}
ChatScreen::ChatScreen()
{
frame = 0;
cursorIndex = 0;
s_historyIndex = -1;
}
void ChatScreen::init()
@@ -24,6 +37,50 @@ void ChatScreen::removed()
void ChatScreen::tick()
{
frame++;
if (cursorIndex > static_cast<int>(message.length()))
cursorIndex = static_cast<int>(message.length());
}
void ChatScreen::handlePasteRequest()
{
wstring pasted = Screen::getClipboard();
for (size_t i = 0; i < pasted.length() && static_cast<int>(message.length()) < SharedConstants::maxChatLength; i++)
{
if (isAllowedChatChar(pasted[i]))
{
message.insert(cursorIndex, 1, pasted[i]);
cursorIndex++;
}
}
}
void ChatScreen::applyHistoryMessage()
{
message = s_historyIndex >= 0 ? s_chatHistory[s_historyIndex] : s_historyDraft;
cursorIndex = static_cast<int>(message.length());
}
void ChatScreen::handleHistoryUp()
{
if (s_chatHistory.empty()) return;
if (s_historyIndex == -1)
{
s_historyDraft = message;
s_historyIndex = static_cast<int>(s_chatHistory.size()) - 1;
}
else if (s_historyIndex > 0)
s_historyIndex--;
applyHistoryMessage();
}
void ChatScreen::handleHistoryDown()
{
if (s_chatHistory.empty()) return;
if (s_historyIndex < static_cast<int>(s_chatHistory.size()) - 1)
s_historyIndex++;
else
s_historyIndex = -1;
applyHistoryMessage();
}
void ChatScreen::keyPressed(wchar_t ch, int eventKey)
@@ -35,31 +92,67 @@ void ChatScreen::keyPressed(wchar_t ch, int eventKey)
}
if (eventKey == Keyboard::KEY_RETURN)
{
wstring msg = trimString(message);
if (msg.length() > 0)
wstring trim = trimString(message);
if (trim.length() > 0)
{
wstring trim = trimString(message);
if (!minecraft->handleClientSideCommand(trim))
{
minecraft->player->chat(trim);
MultiplayerLocalPlayer* mplp = dynamic_cast<MultiplayerLocalPlayer*>(minecraft->player.get());
if (mplp && mplp->connection)
mplp->connection->send(shared_ptr<ChatPacket>(new ChatPacket(trim)));
}
if (s_chatHistory.empty() || s_chatHistory.back() != trim)
{
s_chatHistory.push_back(trim);
if (s_chatHistory.size() > CHAT_HISTORY_MAX)
s_chatHistory.erase(s_chatHistory.begin());
}
}
minecraft->setScreen(NULL);
return;
}
if (eventKey == Keyboard::KEY_BACK && message.length() > 0) message = message.substr(0, message.length() - 1);
if (allowedChars.find(ch) >= 0 && message.length() < SharedConstants::maxChatLength)
if (eventKey == Keyboard::KEY_UP) { handleHistoryUp(); return; }
if (eventKey == Keyboard::KEY_DOWN) { handleHistoryDown(); return; }
if (eventKey == Keyboard::KEY_LEFT)
{
message += ch;
if (cursorIndex > 0)
cursorIndex--;
return;
}
if (eventKey == Keyboard::KEY_RIGHT)
{
if (cursorIndex < static_cast<int>(message.length()))
cursorIndex++;
return;
}
if (eventKey == Keyboard::KEY_BACK && cursorIndex > 0)
{
message.erase(cursorIndex - 1, 1);
cursorIndex--;
return;
}
if (isAllowedChatChar(ch) && static_cast<int>(message.length()) < SharedConstants::maxChatLength)
{
message.insert(cursorIndex, 1, ch);
cursorIndex++;
}
}
void ChatScreen::render(int xm, int ym, float a)
{
fill(2, height - 14, width - 2, height - 2, 0x80000000);
drawString(font, L"> " + message + (frame / 6 % 2 == 0 ? L"_" : L""), 4, height - 12, 0xe0e0e0);
const wstring prefix = L"> ";
int x = 4;
drawString(font, prefix, x, height - 12, 0xe0e0e0);
x += font->width(prefix);
wstring beforeCursor = message.substr(0, cursorIndex);
wstring afterCursor = message.substr(cursorIndex);
drawStringLiteral(font, beforeCursor, x, height - 12, 0xe0e0e0);
x += font->widthLiteral(beforeCursor);
if (frame / 6 % 2 == 0)
drawString(font, L"_", x, height - 12, 0xe0e0e0);
x += font->width(L"_");
drawStringLiteral(font, afterCursor, x, height - 12, 0xe0e0e0);
Screen::render(xm, ym, a);
}
@@ -71,13 +164,15 @@ void ChatScreen::mouseClicked(int x, int y, int buttonNum)
{
if (message.length() > 0 && message[message.length()-1]!=L' ')
{
message += L" ";
message = message.substr(0, cursorIndex) + L" " + message.substr(cursorIndex);
cursorIndex++;
}
message += minecraft->gui->selectedName;
unsigned int maxLength = SharedConstants::maxChatLength;
if (message.length() > maxLength)
size_t nameLen = minecraft->gui->selectedName.length();
size_t insertLen = (message.length() + nameLen <= SharedConstants::maxChatLength) ? nameLen : (SharedConstants::maxChatLength - message.length());
if (insertLen > 0)
{
message = message.substr(0, maxLength);
message = message.substr(0, cursorIndex) + minecraft->gui->selectedName.substr(0, insertLen) + message.substr(cursorIndex);
cursorIndex += static_cast<int>(insertLen);
}
}
else
@@ -85,5 +180,4 @@ void ChatScreen::mouseClicked(int x, int y, int buttonNum)
Screen::mouseClicked(x, y, buttonNum);
}
}
}

View File

@@ -1,21 +1,33 @@
#pragma once
#include "Screen.h"
#include <vector>
using namespace std;
class ChatScreen : public Screen
{
protected:
wstring message;
int cursorIndex;
void applyHistoryMessage();
private:
int frame;
static const size_t CHAT_HISTORY_MAX = 100;
static std::vector<wstring> s_chatHistory;
static int s_historyIndex;
static wstring s_historyDraft;
static const wstring allowedChars;
static bool isAllowedChatChar(wchar_t c);
public:
ChatScreen(); //4J added
ChatScreen();
virtual void init();
virtual void removed();
virtual void tick();
private:
static const wstring allowedChars;
virtual void removed();
virtual void tick();
virtual void handlePasteRequest();
virtual void handleHistoryUp();
virtual void handleHistoryDown();
protected:
void keyPressed(wchar_t ch, int eventKey);
public:

View File

@@ -1441,6 +1441,9 @@ void ClientConnection::handleChat(shared_ptr<ChatPacket> packet)
switch(packet->m_messageType)
{
case ChatPacket::e_ChatCustom:
message = (packet->m_stringArgs.size() >= 1) ? packet->m_stringArgs[0] : L"";
break;
case ChatPacket::e_ChatBedOccupied:
message = app.GetString(IDS_TILE_BED_OCCUPIED);
break;

View File

@@ -55,10 +55,16 @@ void UIComponent_Chat::handleTimerComplete(int id)
float opacity = pGui->getOpacity(m_iPad, i);
if( opacity > 0 )
{
#ifdef _WINDOWS64
// Chat drawn by Gui::render with color codes. Hides Iggy chat to avoid double chats.
m_controlLabelBackground[i].setOpacity(0);
m_labelChatText[i].setOpacity(0);
m_labelChatText[i].setLabel(L"");
#else
m_controlLabelBackground[i].setOpacity(opacity);
m_labelChatText[i].setOpacity(opacity);
m_labelChatText[i].setLabel( pGui->getMessage(m_iPad,i) );
#endif
anyVisible = true;
}
else

View File

@@ -753,10 +753,16 @@ void UIScene_HUD::handleTimerComplete(int id)
float opacity = pGui->getOpacity(m_iPad, i);
if( opacity > 0 )
{
#ifdef _WINDOWS64
// Chat drawn by Gui::render with color codes. Hides Iggy chat to avoid double chats.
m_controlLabelBackground[i].setOpacity(0);
m_labelChatText[i].setOpacity(0);
m_labelChatText[i].setLabel(L"");
#else
m_controlLabelBackground[i].setOpacity(opacity);
m_labelChatText[i].setOpacity(opacity);
m_labelChatText[i].setLabel( pGui->getMessagesCount(m_iPad) ? pGui->getMessage(m_iPad,i) : L"" );
#endif
anyVisible = true;
}
else

View File

@@ -21,6 +21,10 @@ Font::Font(Options *options, const wstring& name, Textures* textures, bool enfor
enforceUnicodeSheet = false;
bidirectional = false;
xPos = yPos = 0.0f;
m_bold = false;
m_italic = false;
m_underline = false;
m_strikethrough = false;
// Set up member variables
m_cols = cols;
@@ -126,6 +130,22 @@ Font::~Font()
}
#endif
void Font::renderStyleLine(float x0, float y0, float x1, float y1)
{
Tesselator* t = Tesselator::getInstance();
float u = 0.0f, v = 0.0f;
t->begin();
t->tex(u, v);
t->vertex(x0, y1, 0.0f);
t->tex(u, v);
t->vertex(x1, y1, 0.0f);
t->tex(u, v);
t->vertex(x1, y0, 0.0f);
t->tex(u, v);
t->vertex(x0, y0, 0.0f);
t->end();
}
void Font::renderCharacter(wchar_t c)
{
float xOff = c % m_cols * m_charWidth;
@@ -137,37 +157,47 @@ void Font::renderCharacter(wchar_t c)
float fontWidth = m_cols * m_charWidth;
float fontHeight = m_rows * m_charHeight;
Tesselator *t = Tesselator::getInstance();
// 4J Stu - Changed to a quad so that we can use within a command buffer
#if 1
const float shear = m_italic ? (height * 0.25f) : 0.0f;
float x0 = xPos, x1 = xPos + width + shear;
float y0 = yPos, y1 = yPos + height;
Tesselator *t = Tesselator::getInstance();
t->begin();
t->tex(xOff / fontWidth, (yOff + 7.99f) / fontHeight);
t->vertex(xPos, yPos + height, 0.0f);
t->vertex(x0, y1, 0.0f);
t->tex((xOff + width) / fontWidth, (yOff + 7.99f) / fontHeight);
t->vertex(xPos + width, yPos + height, 0.0f);
t->vertex(x1, y1, 0.0f);
t->tex((xOff + width) / fontWidth, yOff / fontHeight);
t->vertex(xPos + width, yPos, 0.0f);
t->vertex(x1, y0, 0.0f);
t->tex(xOff / fontWidth, yOff / fontHeight);
t->vertex(xPos, yPos, 0.0f);
t->vertex(x0, y0, 0.0f);
t->end();
#else
t->begin(GL_TRIANGLE_STRIP);
t->tex(xOff / 128.0F, yOff / 128.0F);
t->vertex(xPos, yPos, 0.0f);
t->tex(xOff / 128.0F, (yOff + 7.99f) / 128.0F);
t->vertex(xPos, yPos + 7.99f, 0.0f);
t->tex((xOff + width) / 128.0F, yOff / 128.0F);
t->vertex(xPos + width, yPos, 0.0f);
t->tex((xOff + width) / 128.0F, (yOff + 7.99f) / 128.0F);
t->vertex(xPos + width, yPos + 7.99f, 0.0f);
t->end();
#endif
xPos += (float) charWidths[c];
if (m_bold)
{
float dx = 1.0f;
t->begin();
t->tex(xOff / fontWidth, (yOff + 7.99f) / fontHeight);
t->vertex(x0 + dx, y1, 0.0f);
t->tex((xOff + width) / fontWidth, (yOff + 7.99f) / fontHeight);
t->vertex(x1 + dx, y1, 0.0f);
t->tex((xOff + width) / fontWidth, yOff / fontHeight);
t->vertex(x1 + dx, y0, 0.0f);
t->tex(xOff / fontWidth, yOff / fontHeight);
t->vertex(x0 + dx, y0, 0.0f);
t->end();
}
if (m_underline)
renderStyleLine(x0, y1 - 1.0f, xPos + static_cast<float>(charWidths[c]), y1);
if (m_strikethrough)
{
float mid = y0 + height * 0.5f;
renderStyleLine(x0, mid - 0.5f, xPos + static_cast<float>(charWidths[c]), mid + 0.5f);
}
xPos += static_cast<float>(charWidths[c]);
}
void Font::drawShadow(const wstring& str, int x, int y, int color)
@@ -176,6 +206,26 @@ void Font::drawShadow(const wstring& str, int x, int y, int color)
draw(str, x, y, color, false);
}
void Font::drawShadowLiteral(const wstring& str, int x, int y, int color)
{
int shadowColor = (color & 0xFCFCFC) >> 2 | (color & 0xFF000000);
drawLiteral(str, x + 1, y + 1, shadowColor);
drawLiteral(str, x, y, color);
}
void Font::drawLiteral(const wstring& str, int x, int y, int color)
{
if (str.empty()) return;
if ((color & 0xFC000000) == 0) color |= 0xFF000000;
textures->bindTexture(m_textureLocation);
glColor4f((color >> 16 & 255) / 255.0F, (color >> 8 & 255) / 255.0F, (color & 255) / 255.0F, (color >> 24 & 255) / 255.0F);
xPos = static_cast<float>(x);
yPos = static_cast<float>(y);
wstring cleanStr = sanitize(str);
for (size_t i = 0; i < cleanStr.length(); ++i)
renderCharacter(cleanStr.at(i));
}
void Font::drawShadowWordWrap(const wstring &str, int x, int y, int w, int color, int h)
{
drawWordWrapInternal(str, x + 1, y + 1, w, color, true, h);
@@ -193,24 +243,38 @@ wstring Font::reorderBidi(const wstring &str)
return str;
}
static bool isSectionFormatCode(wchar_t ca)
{
if ((ca >= L'0' && ca <= L'9') || (ca >= L'a' && ca <= L'f') || (ca >= L'A' && ca <= L'F'))
return true;
wchar_t l = static_cast<wchar_t>(ca | 32);
return l == L'l' || l == L'o' || l == L'n' || l == L'm' || l == L'r' || l == L'k';
}
void Font::draw(const wstring &str, bool dropShadow)
{
// Bind the texture
textures->bindTexture(m_textureLocation);
bool noise = false;
m_bold = m_italic = m_underline = m_strikethrough = false;
wstring cleanStr = sanitize(str);
for (int i = 0; i < (int)cleanStr.length(); ++i)
for (int i = 0; i < static_cast<int>(cleanStr.length()); ++i)
{
// Map character
wchar_t c = cleanStr.at(i);
if (c == 167 && i + 1 < cleanStr.length())
{
// 4J - following block was:
// int colorN = L"0123456789abcdefk".indexOf(str.toLowerCase().charAt(i + 1));
wchar_t ca = cleanStr[i+1];
if (!isSectionFormatCode(ca))
{
renderCharacter(167);
renderCharacter(ca);
i += 1;
continue;
}
int colorN = 16;
if(( ca >= L'0' ) && (ca <= L'9')) colorN = ca - L'0';
else if(( ca >= L'a' ) && (ca <= L'f')) colorN = (ca - L'a') + 10;
@@ -218,7 +282,13 @@ void Font::draw(const wstring &str, bool dropShadow)
if (colorN == 16)
{
noise = true;
wchar_t l = static_cast<wchar_t>(ca | 32);
if (l == L'l') m_bold = true;
else if (l == L'o') m_italic = true;
else if (l == L'n') m_underline = true;
else if (l == L'm') m_strikethrough = true;
else if (l == L'r') m_bold = m_italic = m_underline = m_strikethrough = noise = false;
else if (l == L'k') noise = true;
}
else
{
@@ -280,20 +350,34 @@ int Font::width(const wstring& str)
{
wchar_t c = cleanStr.at(i);
if(c == 167)
if (c == 167)
{
// Ignore the character used to define coloured text
++i;
if (i + 1 < cleanStr.length() && isSectionFormatCode(cleanStr[i+1]))
++i;
else
{
len += charWidths[167];
if (i + 1 < cleanStr.length())
len += charWidths[static_cast<unsigned>(cleanStr[++i])];
}
}
else
{
len += charWidths[c];
}
}
return len;
}
int Font::widthLiteral(const wstring& str)
{
wstring cleanStr = sanitize(str);
if (cleanStr == L"") return 0;
int len = 0;
for (size_t i = 0; i < cleanStr.length(); ++i)
len += charWidths[static_cast<unsigned>(cleanStr.at(i))];
return len;
}
wstring Font::sanitize(const wstring& str)
{
wstring sb = str;
@@ -467,7 +551,7 @@ void Font::setBidirectional(bool bidirectional)
bool Font::AllCharactersValid(const wstring &str)
{
for (int i = 0; i < (int)str.length(); ++i)
for (int i = 0; i < static_cast<int>(str.length()); ++i)
{
wchar_t c = str.at(i);
@@ -512,15 +596,15 @@ void Font::renderFakeCB(IntBuffer *ib)
float uo = (0.0f) / 128.0f;
float vo = (0.0f) / 128.0f;
t->vertexUV((float)(0), (float)( 0 + s), (float)( 0), (float)( ix / 128.0f + uo), (float)( (iy + s) / 128.0f + vo));
t->vertexUV((float)(0 + s), (float)( 0 + s), (float)( 0), (float)( (ix + s) / 128.0f + uo), (float)( (iy + s) / 128.0f + vo));
t->vertexUV((float)(0 + s), (float)( 0), (float)( 0), (float)( (ix + s) / 128.0f + uo), (float)( iy / 128.0f + vo));
t->vertexUV((float)(0), (float)( 0), (float)( 0), (float)( ix / 128.0f + uo), (float)( iy / 128.0f + vo));
t->vertexUV(static_cast<float>(0), static_cast<float>(0 + s), static_cast<float>(0), static_cast<float>(ix / 128.0f + uo), static_cast<float>((iy + s) / 128.0f + vo));
t->vertexUV(static_cast<float>(0 + s), static_cast<float>(0 + s), static_cast<float>(0), static_cast<float>((ix + s) / 128.0f + uo), static_cast<float>((iy + s) / 128.0f + vo));
t->vertexUV(static_cast<float>(0 + s), static_cast<float>(0), static_cast<float>(0), static_cast<float>((ix + s) / 128.0f + uo), static_cast<float>(iy / 128.0f + vo));
t->vertexUV(static_cast<float>(0), static_cast<float>(0), static_cast<float>(0), static_cast<float>(ix / 128.0f + uo), static_cast<float>(iy / 128.0f + vo));
// target.colorBlit(texture, x + xo, y, color, ix, iy,
// charWidths[chars[i]], 8);
t->end();
glTranslatef((float)charWidths[i], 0, 0);
glTranslatef(static_cast<float>(charWidths[i]), 0, 0);
}
else
{

View File

@@ -21,6 +21,12 @@ private:
float xPos;
float yPos;
// § format state (bold, italic, underline, strikethrough); set during draw(), reset by §r
bool m_bold;
bool m_italic;
bool m_underline;
bool m_strikethrough;
bool enforceUnicodeSheet; // use unicode sheet for ascii
bool bidirectional; // use bidi to flip strings
@@ -41,9 +47,11 @@ public:
private:
void renderCharacter(wchar_t c); // 4J added
void renderStyleLine(float x0, float y0, float x1, float y1); // solid line for underline/strikethrough
public:
void drawShadow(const wstring& str, int x, int y, int color);
void drawShadowLiteral(const wstring& str, int x, int y, int color); // draw without interpreting § codes
void drawShadowWordWrap(const wstring &str, int x, int y, int w, int color, int h); // 4J Added h param
void draw(const wstring &str, int x, int y, int color);
/**
@@ -58,11 +66,13 @@ private:
void draw(const wstring &str, bool dropShadow);
void draw(const wstring& str, int x, int y, int color, bool dropShadow);
void drawLiteral(const wstring& str, int x, int y, int color); // no § parsing
int MapCharacter(wchar_t c); // 4J added
bool CharacterExists(wchar_t c); // 4J added
public:
int width(const wstring& str);
int widthLiteral(const wstring& str); // width without skipping § codes (for chat input)
wstring sanitize(const wstring& str);
void drawWordWrap(const wstring &string, int x, int y, int w, int col, int h); // 4J Added h param

View File

@@ -971,15 +971,10 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_ALPHA_TEST);
// 4J Stu - We have moved the chat text to a xui
#if 0
#if defined(_WINDOWS64)
glPushMatrix();
// 4J-PB we need to move this up a bit because we've moved the quick select
//glTranslatef(0, ((float)screenHeight) - 48, 0);
glTranslatef(0.0f, (float)(screenHeight - iSafezoneYHalf - iTooltipsYOffset - 16 - 3 + 22) - 24.0f, 0.0f);
// glScalef(1.0f / ssc.scale, 1.0f / ssc.scale, 1);
glTranslatef(0.0f, static_cast<float>(screenHeight - iSafezoneYHalf - iTooltipsYOffset - 16 - 3 + 22) - 24.0f, 0.0f);
// 4J-PB - we need gui messages for each of the possible 4 splitscreen players
if(bDisplayGui)
{
int iPad=minecraft->player->GetXboxPad();
@@ -993,23 +988,21 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
if (t < 0) t = 0;
if (t > 1) t = 1;
t = t * t;
int alpha = (int) (255 * t);
int alpha = static_cast<int>(255 * t);
if (isChatting) alpha = 255;
if (alpha > 0)
{
int x = iSafezoneXHalf+2;
int y = -((int)i) * 9;
int y = -(static_cast<int>(i)) * 9;
if(bTwoPlayerSplitscreen)
{
y+= iHeightOffset;
}
wstring msg = guiMessages[iPad][i].string;
// 4J-PB - fill the black bar across the whole screen, otherwise it looks odd due to the safe area
this->fill(0, y - 1, screenWidth/fScaleFactorWidth, y + 8, (alpha / 2) << 24);
glEnable(GL_BLEND);
font->drawShadow(msg, iSafezoneXHalf+4, y, 0xffffff + (alpha << 24));
}
}

View File

@@ -48,10 +48,10 @@ void GuiComponent::fill(int x0, int y0, int x1, int y1, int col)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(r, g, b, a);
t->begin();
t->vertex((float)(x0), (float)( y1), (float)( 0));
t->vertex((float)(x1), (float)( y1), (float)( 0));
t->vertex((float)(x1), (float)( y0), (float)( 0));
t->vertex((float)(x0), (float)( y0), (float)( 0));
t->vertex(static_cast<float>(x0), static_cast<float>(y1), static_cast<float>(0));
t->vertex(static_cast<float>(x1), static_cast<float>(y1), static_cast<float>(0));
t->vertex(static_cast<float>(x1), static_cast<float>(y0), static_cast<float>(0));
t->vertex(static_cast<float>(x0), static_cast<float>(y0), static_cast<float>(0));
t->end();
glEnable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
@@ -77,11 +77,11 @@ void GuiComponent::fillGradient(int x0, int y0, int x1, int y1, int col1, int co
Tesselator *t = Tesselator::getInstance();
t->begin();
t->color(r1, g1, b1, a1);
t->vertex((float)(x1), (float)( y0), blitOffset);
t->vertex((float)(x0), (float)( y0), blitOffset);
t->vertex(static_cast<float>(x1), static_cast<float>(y0), blitOffset);
t->vertex(static_cast<float>(x0), static_cast<float>(y0), blitOffset);
t->color(r2, g2, b2, a2);
t->vertex((float)(x0), (float)( y1), blitOffset);
t->vertex((float)(x1), (float)( y1), blitOffset);
t->vertex(static_cast<float>(x0), static_cast<float>(y1), blitOffset);
t->vertex(static_cast<float>(x1), static_cast<float>(y1), blitOffset);
t->end();
glShadeModel(GL_FLAT);
@@ -105,6 +105,11 @@ void GuiComponent::drawString(Font *font, const wstring& str, int x, int y, int
font->drawShadow(str, x, y, color);
}
void GuiComponent::drawStringLiteral(Font *font, const wstring& str, int x, int y, int color)
{
font->drawShadowLiteral(str, x, y, color);
}
void GuiComponent::blit(int x, int y, int sx, int sy, int w, int h)
{
float us = 1 / 256.0f;
@@ -117,20 +122,20 @@ void GuiComponent::blit(int x, int y, int sx, int sy, int w, int h)
const float extraShift = 0.75f;
// 4J - subtracting extraShift (actual screen pixels, so need to compensate for physical & game width) from each x & y coordinate to compensate for centre of pixels in directx vs openGL
float dx = ( extraShift * (float)Minecraft::GetInstance()->width ) / (float)Minecraft::GetInstance()->width_phys;
float dx = ( extraShift * static_cast<float>(Minecraft::GetInstance()->width) ) / static_cast<float>(Minecraft::GetInstance()->width_phys);
// 4J - Also factor in the scaling from gui coordinate space to the screen. This varies based on user-selected gui scale, and whether we are in a viewport mode or not
dx /= Gui::currentGuiScaleFactor;
float dy = extraShift / Gui::currentGuiScaleFactor;
// Ensure that the x/y, width and height are actually pixel aligned at our current scale factor - in particular, for split screen mode with the default (3X)
// scale, we have an overall scale factor of 3 * 0.5 = 1.5, and so any odd pixels won't align
float fx = (floorf((float)x * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor;
float fy = (floorf((float)y * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor;
float fw = (floorf((float)w * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor;
float fh = (floorf((float)h * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor;
float fx = (floorf(static_cast<float>(x) * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor;
float fy = (floorf(static_cast<float>(y) * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor;
float fw = (floorf(static_cast<float>(w) * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor;
float fh = (floorf(static_cast<float>(h) * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor;
t->vertexUV(fx + 0 - dx, fy + fh - dy, (float)( blitOffset), (float)( (sx + 0) * us), (float)( (sy + h) * vs));
t->vertexUV(fx + fw - dx, fy + fh - dy, (float)( blitOffset), (float)( (sx + w) * us), (float)( (sy + h) * vs));
t->vertexUV(fx + fw - dx, fy + 0 - dy, (float)( blitOffset), (float)( (sx + w) * us), (float)( (sy + 0) * vs));
t->vertexUV(fx + 0 - dx, fy + 0 - dy, (float)( blitOffset), (float)( (sx + 0) * us), (float)( (sy + 0) * vs));
t->vertexUV(fx + 0 - dx, fy + fh - dy, static_cast<float>(blitOffset), static_cast<float>((sx + 0) * us), static_cast<float>((sy + h) * vs));
t->vertexUV(fx + fw - dx, fy + fh - dy, static_cast<float>(blitOffset), static_cast<float>((sx + w) * us), static_cast<float>((sy + h) * vs));
t->vertexUV(fx + fw - dx, fy + 0 - dy, static_cast<float>(blitOffset), static_cast<float>((sx + w) * us), static_cast<float>((sy + 0) * vs));
t->vertexUV(fx + 0 - dx, fy + 0 - dy, static_cast<float>(blitOffset), static_cast<float>((sx + 0) * us), static_cast<float>((sy + 0) * vs));
t->end();
}

View File

@@ -15,5 +15,6 @@ public:
GuiComponent(); // 4J added
void drawCenteredString(Font *font, const wstring& str, int x, int y, int color);
void drawString(Font *font, const wstring& str, int x, int y, int color);
void drawStringLiteral(Font* font, const wstring& str, int x, int y, int color);
void blit(int x, int y, int sx, int sy, int w, int h);
};

View File

@@ -21,6 +21,8 @@
#include "..\Minecraft.World\AABB.h"
#include "..\Minecraft.World\Pos.h"
#include "..\Minecraft.World\SharedConstants.h"
#include "..\Minecraft.World\ChatPacket.h"
#include "..\Minecraft.World\StringHelpers.h"
#include "..\Minecraft.World\Socket.h"
#include "..\Minecraft.World\Achievements.h"
#include "..\Minecraft.World\net.minecraft.h"
@@ -607,38 +609,26 @@ void PlayerConnection::handleSetCarriedItem(shared_ptr<SetCarriedItemPacket> pac
void PlayerConnection::handleChat(shared_ptr<ChatPacket> packet)
{
// 4J - TODO
#if 0
wstring message = packet->message;
if (packet->m_stringArgs.empty()) return;
wstring message = trimString(packet->m_stringArgs[0]);
if (message.length() > SharedConstants::maxChatLength)
{
disconnect(L"Chat message too long");
disconnect(DisconnectPacket::eDisconnect_None); // or a specific reason
return;
}
message = message.trim();
for (int i = 0; i < message.length(); i++)
{
if (SharedConstants.acceptableLetters.indexOf(message.charAt(i)) < 0 && (int) message.charAt(i) < 32)
{
disconnect(L"Illegal characters in chat");
return;
}
}
if (message.startsWith("/"))
// Optional: validate characters (acceptableLetters)
if (message.length() > 0 && message[0] == L'/')
{
handleCommand(message);
} else {
message = "<" + player.name + "> " + message;
logger.info(message);
server.players.broadcastAll(new ChatPacket(message));
return;
}
wstring formatted = L"<" + player->name + L"> " + message;
server->getPlayers()->broadcastAll(shared_ptr<ChatPacket>(new ChatPacket(formatted)));
chatSpamTickCount += SharedConstants::TICKS_PER_SECOND;
if (chatSpamTickCount > SharedConstants::TICKS_PER_SECOND * 10)
{
disconnect("disconnect.spam");
disconnect(DisconnectPacket::eDisconnect_None); // spam
}
#endif
}
void PlayerConnection::handleCommand(const wstring& message)

View File

@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "Screen.h"
#include "Button.h"
#include "ChatScreen.h"
#include "GuiParticles.h"
#include "Tesselator.h"
#include "Textures.h"
@@ -42,13 +43,32 @@ void Screen::keyPressed(wchar_t eventCharacter, int eventKey)
wstring Screen::getClipboard()
{
// 4J - removed
return NULL;
#ifdef _WINDOWS64
if (!OpenClipboard(NULL)) return wstring();
HANDLE h = GetClipboardData(CF_UNICODETEXT);
wstring out;
if (h)
{
const wchar_t *p = reinterpret_cast<const wchar_t*>(GlobalLock(h));
if (p) { out = p; GlobalUnlock(h); }
}
CloseClipboard();
return out;
#else
return wstring();
#endif
}
void Screen::setClipboard(const wstring& str)
{
// 4J - removed
#ifdef _WINDOWS64
if (!OpenClipboard(NULL)) return;
EmptyClipboard();
size_t len = (str.length() + 1) * sizeof(wchar_t);
HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, len);
if (h) { memcpy(GlobalLock(h), str.c_str(), len); GlobalUnlock(h); SetClipboardData(CF_UNICODETEXT, h); }
CloseClipboard();
#endif
}
void Screen::mouseClicked(int x, int y, int buttonNum)
@@ -121,34 +141,88 @@ void Screen::updateEvents()
}
}
// Poll keyboard events
// Only drain WM_CHAR when this screen wants text input (e.g. ChatScreen); otherwise we'd steal keys from the game
if (dynamic_cast<ChatScreen*>(this) != nullptr)
{
wchar_t ch;
while (g_KBMInput.ConsumeChar(ch))
{
if (ch >= 0x20)
keyPressed(ch, -1);
else if (ch == 0x08)
keyPressed(0, Keyboard::KEY_BACK);
else if (ch == 0x0D)
keyPressed(0, Keyboard::KEY_RETURN);
}
}
// Arrow key repeat: deliver on first press (when key down and last==0) and while held (throttled)
static DWORD s_arrowLastTime[2] = { 0, 0 };
static bool s_arrowFirstRepeat[2] = { false, false };
const DWORD ARROW_REPEAT_DELAY_MS = 250;
const DWORD ARROW_REPEAT_INTERVAL_MS = 50;
DWORD now = GetTickCount();
// Poll keyboard events (special keys that may not come through WM_CHAR, e.g. Escape, arrows)
for (int vk = 0; vk < 256; vk++)
{
if (g_KBMInput.IsKeyPressed(vk))
bool deliver = g_KBMInput.IsKeyPressed(vk);
if (vk == VK_LEFT || vk == VK_RIGHT)
{
// Map Windows virtual key to the Keyboard constants used by Screen::keyPressed
int mappedKey = -1;
wchar_t ch = 0;
if (vk == VK_ESCAPE) mappedKey = Keyboard::KEY_ESCAPE;
else if (vk == VK_RETURN) mappedKey = Keyboard::KEY_RETURN;
else if (vk == VK_BACK) mappedKey = Keyboard::KEY_BACK;
else if (vk == VK_UP) mappedKey = Keyboard::KEY_UP;
else if (vk == VK_DOWN) mappedKey = Keyboard::KEY_DOWN;
else if (vk == VK_LEFT) mappedKey = Keyboard::KEY_LEFT;
else if (vk == VK_RIGHT) mappedKey = Keyboard::KEY_RIGHT;
else if (vk == VK_LSHIFT || vk == VK_RSHIFT) mappedKey = Keyboard::KEY_LSHIFT;
else if (vk == VK_TAB) mappedKey = Keyboard::KEY_TAB;
else if (vk >= 'A' && vk <= 'Z')
int idx = (vk == VK_LEFT) ? 0 : 1;
if (!g_KBMInput.IsKeyDown(vk))
{
ch = (wchar_t)(vk - 'A' + L'a');
if (g_KBMInput.IsKeyDown(VK_LSHIFT) || g_KBMInput.IsKeyDown(VK_RSHIFT)) ch = (wchar_t)vk;
s_arrowLastTime[idx] = 0;
s_arrowFirstRepeat[idx] = false;
}
else
{
DWORD last = s_arrowLastTime[idx];
if (last == 0)
deliver = true;
else if (!deliver)
{
DWORD interval = s_arrowFirstRepeat[idx] ? ARROW_REPEAT_INTERVAL_MS : ARROW_REPEAT_DELAY_MS;
if ((now - last) >= interval)
{
deliver = true;
s_arrowFirstRepeat[idx] = true;
}
}
if (deliver)
s_arrowLastTime[idx] = now;
}
else if (vk >= '0' && vk <= '9') ch = (wchar_t)vk;
else if (vk == VK_SPACE) ch = L' ';
if (mappedKey != -1) keyPressed(ch, mappedKey);
else if (ch != 0) keyPressed(ch, -1);
}
// Escape: deliver when key is down so we don't miss it (IsKeyPressed can be one frame late)
if (vk == VK_ESCAPE && g_KBMInput.IsKeyDown(VK_ESCAPE))
deliver = true;
if (!deliver) continue;
if (dynamic_cast<ChatScreen*>(this) != nullptr &&
(vk >= 'A' && vk <= 'Z' || vk >= '0' && vk <= '9' || vk == VK_SPACE || vk == VK_RETURN || vk == VK_BACK))
continue;
// Map to Screen::keyPressed
int mappedKey = -1;
wchar_t ch = 0;
if (vk == VK_ESCAPE) mappedKey = Keyboard::KEY_ESCAPE;
else if (vk == VK_RETURN) mappedKey = Keyboard::KEY_RETURN;
else if (vk == VK_BACK) mappedKey = Keyboard::KEY_BACK;
else if (vk == VK_UP) mappedKey = Keyboard::KEY_UP;
else if (vk == VK_DOWN) mappedKey = Keyboard::KEY_DOWN;
else if (vk == VK_LEFT) mappedKey = Keyboard::KEY_LEFT;
else if (vk == VK_RIGHT) mappedKey = Keyboard::KEY_RIGHT;
else if (vk == VK_LSHIFT || vk == VK_RSHIFT) mappedKey = Keyboard::KEY_LSHIFT;
else if (vk == VK_TAB) mappedKey = Keyboard::KEY_TAB;
else if (vk >= 'A' && vk <= 'Z')
{
ch = static_cast<wchar_t>(vk - 'A' + L'a');
if (g_KBMInput.IsKeyDown(VK_LSHIFT) || g_KBMInput.IsKeyDown(VK_RSHIFT)) ch = static_cast<wchar_t>(vk);
}
else if (vk >= '0' && vk <= '9') ch = static_cast<wchar_t>(vk);
else if (vk == VK_SPACE) ch = L' ';
if (mappedKey != -1) keyPressed(ch, mappedKey);
else if (ch != 0) keyPressed(ch, -1);
}
#else
/* 4J - TODO
@@ -232,10 +306,10 @@ void Screen::renderDirtBackground(int vo)
float s = 32;
t->begin();
t->color(0x404040);
t->vertexUV((float)(0), (float)( height), (float)( 0), (float)( 0), (float)( height / s + vo));
t->vertexUV((float)(width), (float)( height), (float)( 0), (float)( width / s), (float)( height / s + vo));
t->vertexUV((float)(width), (float)( 0), (float)( 0), (float)( width / s), (float)( 0 + vo));
t->vertexUV((float)(0), (float)( 0), (float)( 0), (float)( 0), (float)( 0 + vo));
t->vertexUV(static_cast<float>(0), static_cast<float>(height), static_cast<float>(0), static_cast<float>(0), static_cast<float>(height / s + vo));
t->vertexUV(static_cast<float>(width), static_cast<float>(height), static_cast<float>(0), static_cast<float>(width / s), static_cast<float>(height / s + vo));
t->vertexUV(static_cast<float>(width), static_cast<float>(0), static_cast<float>(0), static_cast<float>(width / s), static_cast<float>(0 + vo));
t->vertexUV(static_cast<float>(0), static_cast<float>(0), static_cast<float>(0), static_cast<float>(0), static_cast<float>(0 + vo));
t->end();
#endif
}

View File

@@ -36,9 +36,12 @@ protected:
virtual void mouseReleased(int x, int y, int buttonNum);
virtual void buttonClicked(Button *button);
public:
virtual void init(Minecraft *minecraft, int width, int height);
virtual void init(Minecraft *minecraft, int width, int height);
virtual void setSize(int width, int height);
virtual void init();
virtual void handlePasteRequest() {}
virtual void handleHistoryUp() {}
virtual void handleHistoryDown() {}
virtual void updateEvents();
virtual void mouseEvent();
virtual void keyboardEvent();

View File

@@ -257,8 +257,8 @@ bool KeyboardMouseInput::IsMouseButtonReleased(int button) const
void KeyboardMouseInput::ConsumeMouseDelta(float &dx, float &dy)
{
dx = (float)m_mouseDeltaAccumX;
dy = (float)m_mouseDeltaAccumY;
dx = static_cast<float>(m_mouseDeltaAccumX);
dy = static_cast<float>(m_mouseDeltaAccumY);
m_mouseDeltaAccumX = 0;
m_mouseDeltaAccumY = 0;
}
@@ -375,12 +375,12 @@ float KeyboardMouseInput::GetMoveY() const
float KeyboardMouseInput::GetLookX(float sensitivity) const
{
return (float)m_mouseDeltaX * sensitivity;
return static_cast<float>(m_mouseDeltaX) * sensitivity;
}
float KeyboardMouseInput::GetLookY(float sensitivity) const
{
return (float)(-m_mouseDeltaY) * sensitivity;
return static_cast<float>(-m_mouseDeltaY) * sensitivity;
}
void KeyboardMouseInput::OnChar(wchar_t c)

View File

@@ -143,4 +143,4 @@ private:
extern KeyboardMouseInput g_KBMInput;
#endif // _WINDOWS64
#endif

View File

@@ -22,6 +22,9 @@
#include "..\..\Minecraft.World\net.minecraft.world.level.tile.h"
#include "..\ClientConnection.h"
#include "..\Minecraft.h"
#include "..\ChatScreen.h"
#include "KeyboardMouseInput.h"
#include "..\User.h"
#include "..\..\Minecraft.World\Socket.h"
#include "..\..\Minecraft.World\ThreadName.h"
@@ -572,14 +575,28 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_CHAR:
// Buffer typed characters so UIScene_Keyboard can dispatch them to the Iggy Flash player
if (wParam >= 0x20 || wParam == 0x08 || wParam == 0x0D) // printable chars + backspace + enter
g_KBMInput.OnChar((wchar_t)wParam);
g_KBMInput.OnChar(static_cast<wchar_t>(wParam));
break;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
{
int vk = (int)wParam;
if (lParam & 0x40000000) break; // ignore auto-repeat
int vk = static_cast<int>(wParam);
if ((lParam & 0x40000000) && vk != VK_LEFT && vk != VK_RIGHT && vk != VK_BACK)
break;
#ifdef _WINDOWS64
Minecraft* pm = Minecraft::GetInstance();
ChatScreen* chat = pm && pm->screen ? dynamic_cast<ChatScreen*>(pm->screen) : nullptr;
if (chat)
{
if (vk == 'V' && (GetKeyState(VK_CONTROL) & 0x8000))
{ chat->handlePasteRequest(); break; }
if ((vk == VK_UP || vk == VK_DOWN) && !(lParam & 0x40000000))
{ if (vk == VK_UP) chat->handleHistoryUp(); else chat->handleHistoryDown(); break; }
if (vk >= '1' && vk <= '9') // Prevent hotkey conflicts
break;
}
#endif
if (vk == VK_SHIFT)
vk = (MapVirtualKey((lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX) == VK_RSHIFT) ? VK_RSHIFT : VK_LSHIFT;
else if (vk == VK_CONTROL)
@@ -592,7 +609,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_KEYUP:
case WM_SYSKEYUP:
{
int vk = (int)wParam;
int vk = static_cast<int>(wParam);
if (vk == VK_SHIFT)
vk = (MapVirtualKey((lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX) == VK_RSHIFT) ? VK_RSHIFT : VK_LSHIFT;
else if (vk == VK_CONTROL)
@@ -1612,6 +1629,14 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
}
// Open chat
if (g_KBMInput.IsKeyPressed('T') && app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL)
{
g_KBMInput.ClearCharBuffer();
pMinecraft->setScreen(new ChatScreen());
SetFocus(g_hWnd);
}
#if 0
// has the game defined profile data been changed (by a profile load)
if(app.uiGameDefinedDataChangedBitmask!=0)