Skip to content

Commit

Permalink
Added ChunkLock visualisation. (mrkite#369)
Browse files Browse the repository at this point in the history
  • Loading branch information
madmaxoft authored Nov 25, 2023
1 parent 133027c commit aca11d0
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 3 deletions.
64 changes: 62 additions & 2 deletions chunk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Chunk::Chunk()
, loaded(false)
, rendering(false)
, inhabitedTime(0)
, isChunkLocked(false)
{}

Chunk::~Chunk() {
Expand Down Expand Up @@ -348,14 +349,73 @@ void Chunk::loadEntities(const NBT &nbt) {
auto entitylist = nbt.at("Entities");
int numEntities = entitylist->length();
for (int i = 0; i < numEntities; ++i) {
auto e = Entity::TryParse(entitylist->at(i));
if (e)
auto entityNbt = entitylist->at(i);
auto e = Entity::TryParse(entityNbt);
if (e) {
entities.insert(e->type(), e);

// Check for ChunkLock-related entities:
loadCheckEntityChunkLock(entityNbt);
}
}
}
}
}

void Chunk::loadCheckEntityChunkLock(const Tag * entityNbt)
{
/* ChunkLock uses a "minecraft:marker" entity to store the state:
- the entity has a "chunklock" Tag
- the entity has a Tag either "locked" or "unlocked"
- the entity has "data"."source"."id" data specifying the item needed for unlocking
- the item count needed for unlocking is stored in scoreboard, not in the entity itself (!)
*/

// Check if this is a "locked" marker:
if (entityNbt->at("id")->toString() != "minecraft:marker") {
return;
}
auto tags = dynamic_cast<const Tag_List *>(entityNbt->at("Tags"));
if (tags == nullptr) {
return;
}
auto len = tags->length();
bool hasChunklockTag = false;
bool hasLockedTag = false;
for (int i = 0; i < len; ++i)
{
auto tagStr = dynamic_cast<const Tag_String *>(tags->at(i));
if (tagStr == nullptr) {
continue;
}
auto tagStrVal = tagStr->toString();
if (tagStrVal == "chunklock") {
hasChunklockTag = true;
} else if (tagStrVal == "locked") {
hasLockedTag = true;
}
}
this->isChunkLocked = hasChunklockTag && hasLockedTag;
if (!this->isChunkLocked) {
return;
}

// Find the name of the item needed for unlocking:
auto tData = dynamic_cast<const Tag_Compound *>(entityNbt->at("data"));
if (tData == nullptr) {
return;
}
auto tDataSource = dynamic_cast<const Tag_Compound *>(tData->at("source"));
if (tDataSource == nullptr) {
return;
}
auto tId = dynamic_cast<const Tag_String *>(tDataSource->at("id"));
if (tId != nullptr) {
this->chunkLockItemName = tId->toString();
}
}



// supported DataVersions:
// 0 = 1.8 and below
Expand Down
18 changes: 18 additions & 0 deletions chunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ class Chunk : public QObject {
typedef QMultiMap<QString, QSharedPointer<OverlayItem>> EntityMap;
const EntityMap& getEntityMap() const;

/** Returns whether the chunk is locked by the ChunkLock resourcepack. */
bool getIsChunkLocked() const { return isChunkLocked; }

/** Returns the name of the item needed for unlocking the ChunkLock.
Only valid if getIsChunkLocked() returns true. */
const QString & getChunkLockItemName() const { return chunkLockItemName; }

signals:
void structureFound(QSharedPointer<GeneratedStructure> structure);

Expand All @@ -89,6 +96,14 @@ class Chunk : public QObject {
uchar image[16 * 16 * 4]; // cached render: RGBA for 16*16 Blocks
short depth[16 * 16]; // cached depth map to create shadow
EntityMap entities;

/** Specifies whether the chunk is locked by the ChunkLock resourcepack. */
bool isChunkLocked;

/** The name of the item needed for unlocking the chunk.
Only valid if isChunkLocked is true. */
QString chunkLockItemName;

friend class MapView;
friend class ChunkRenderer;
friend class ChunkCache;
Expand All @@ -101,6 +116,9 @@ class Chunk : public QObject {
void loadSection_createDummyPalette(ChunkSection * cs);
void loadSection_loadBlockStates(ChunkSection *cs, const Tag * blockStateTag);
bool loadSection_decodeBiomePalette(ChunkSection * cs, const Tag * biomesTag);

/** Checks whether the specified entity NBT is relevant to ChunkLock; if so, updates the ChunkLock-related state. */
void loadCheckEntityChunkLock(const Tag * entityNbt);
};

#endif // CHUNK_H_
4 changes: 4 additions & 0 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ int main(int argc, char *argv[]) {
minutor.setViewInhabitedTime(true);
continue;
}
if (args[i] == "-chl" || args[i] == "--chunklock") {
minutor.setViewChunkLock(true);
continue;
}
}

minutor.show();
Expand Down
14 changes: 14 additions & 0 deletions mapview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,17 @@ void MapView::drawChunk(int x, int z) {
if (this->zoom < 1.0)
canvas.setRenderHint(QPainter::SmoothPixmapTransform);
canvas.drawImage(targetRect, srcImage);

// Draw the ChunkLock overlay:
if (this->flags & flgChunkLock) {
if (chunk && chunk->getIsChunkLocked()) {
canvas.setPen(QColor::fromRgb(0xff0000));
canvas.setBrush(Qt::transparent);
double xAdj = (centerx > 0) ? 1 : 2; // Adjustments are needed for chunks crossing the X or Y axis in viewspace
double yAdj = (centery > 0) ? 1 : 2; // Otherwise their bottom / right edges are missing, most likely due to rounding.
canvas.drawRect(centerx, centery, chunksize - xAdj, chunksize - yAdj);
}
}
}

void MapView::getToolTip(int x, int z) {
Expand Down Expand Up @@ -616,6 +627,9 @@ void MapView::getToolTip(int x, int z) {
hovertext += " Zoom:" + QString().number(zoomLevel);
#endif

if (chunk && chunk->getIsChunkLocked()) {
hovertext += QString(" (ChunkLocked, needs %1)").arg(chunk->getChunkLockItemName());
}
emit hoverTextChanged(hovertext);
}

Expand Down
3 changes: 2 additions & 1 deletion mapview.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class MapView : public QWidget {
flgSeaGround = 1 << 5,
flgSingleLayer = 1 << 6,
flgSlimeChunks = 1 << 7,
flgInhabitedTime = 1 << 8
flgInhabitedTime = 1 << 8,
flgChunkLock = 1 << 9,
};

typedef struct struct_BlockLocation {
Expand Down
15 changes: 15 additions & 0 deletions minutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@ void Minutor::setViewInhabitedTime(bool value) {
toggleFlags();
}

void Minutor::setViewChunkLock(bool value) {
m_ui.action_ChunkLock->setChecked(value);
toggleFlags();
}

void Minutor::setDepth(int value) {
depth->setValue(value);
}
Expand All @@ -340,6 +345,7 @@ void Minutor::toggleFlags() {
if (m_ui.action_SingleLayer->isChecked()) flags |= MapView::flgSingleLayer;
if (m_ui.action_SlimeChunks->isChecked()) flags |= MapView::flgSlimeChunks;
if (m_ui.action_InhabitedTime->isChecked()) flags |= MapView::flgInhabitedTime;
if (m_ui.action_ChunkLock->isChecked()) flags |= MapView::flgChunkLock;
mapview->setFlags(flags);
mapview->redraw();
}
Expand Down Expand Up @@ -512,6 +518,11 @@ void Minutor::about() {
.arg(qApp->organizationName()));
}

void Minutor::updateDatapackActions()
{
m_ui.action_ChunkLock->setVisible(WorldInfo::Instance().isDatapackEnabled("chunklock"));
}

void Minutor::updateDimensions() {
WorldInfo::Instance().getDimensionsInWorld(currentWorld, m_ui.menu_Dimension, this);
}
Expand Down Expand Up @@ -575,6 +586,9 @@ void Minutor::createActions() {
connect(m_ui.action_InhabitedTime, SIGNAL(triggered()),
this, SLOT(toggleFlags()));

connect(m_ui.action_ChunkLock, SIGNAL(triggered()),
this, SLOT(toggleFlags()));

// [View->Others]
// m_ui.action_Refresh->setStatusTip(tr("Reloads all chunks, "
// "but keeps the same position / dimension"));
Expand Down Expand Up @@ -779,6 +793,7 @@ void Minutor::loadWorld(QDir path) {
WorldInfo & wi(WorldInfo::Instance());
wi.parseWorldFolder(path);
wi.parseWorldInfo();
updateDatapackActions();

// add level name to window title
setWindowTitle(qApp->applicationName() + " - " + wi.getLevelName());
Expand Down
4 changes: 4 additions & 0 deletions minutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class Minutor : public QMainWindow {
void setViewSingleLayer(bool value); // set View->Single_Layer
void setViewSlimeChunks(bool value); // set View->Slime_Chunks
void setViewInhabitedTime(bool value); // set View->Inhabited_Time
void setViewChunkLock(bool value); // set View->ChunkLock
void setDepth(int value); // set Depth-Slider

MapView *getMapview() const;
Expand All @@ -81,6 +82,9 @@ private slots:

void about();

/** Updates the visibility of datapack-specific actions based on the current world's loaded datapacks. */
void updateDatapackActions();

void updateDimensions();
void rescanWorlds();
void saveProgress(QString status, double value);
Expand Down
13 changes: 13 additions & 0 deletions minutor.ui
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
<addaction name="action_SlimeChunks"/>
<addaction name="action_InhabitedTime"/>
<addaction name="separator"/>
<addaction name="action_ChunkLock"/>
<addaction name="separator"/>
<addaction name="action_Refresh"/>
</widget>
<widget class="QMenu" name="menu_Overlay">
Expand Down Expand Up @@ -393,6 +395,17 @@ but keeps the same position / dimension</string>
<string>Statistic for Block</string>
</property>
</action>
<action name="action_ChunkLock">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>ChunkLock</string>
</property>
<property name="toolTip">
<string>Displays chunks locked by ChunkLock (if installed)</string>
</property>
</action>
</widget>
<resources/>
<connections/>
Expand Down

0 comments on commit aca11d0

Please sign in to comment.