Something went wrong. Try again.
the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
Something went wrong. Try again.
19 kB · 661 lines
C++
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662#include "stdafx.h"#include "PistonBaseTile.h"#include "PistonMovingPiece.h"#include "PistonPieceEntity.h"#include "PistonExtensionTile.h"#include "Facing.h"#include "net.minecraft.world.level.h"#include "..\Minecraft.Client\Minecraft.h"#include "..\Minecraft.Client\MultiPlayerLevel.h"#include "net.minecraft.world.h"#include "LevelChunk.h"#include "Dimension.h"
const wstring PistonBaseTile::EDGE_TEX = L"piston_side";const wstring PistonBaseTile::PLATFORM_TEX = L"piston_top";const wstring PistonBaseTile::PLATFORM_STICKY_TEX = L"piston_top_sticky";const wstring PistonBaseTile::BACK_TEX = L"piston_bottom";const wstring PistonBaseTile::INSIDE_TEX = L"piston_inner_top";
const float PistonBaseTile::PLATFORM_THICKNESS = 4.0f;
DWORD PistonBaseTile::tlsIdx = TlsAlloc();
// 4J - NOTE - this ignoreUpdate stuff has been removed from the java version, but I'm not currently sure how the java version does without it... there must be// some other mechanism that we don't have that stops the event from one piston being processed, from causing neighbours to have extra events created for them.// For us, that means that if we create a piston next to another one, then one of them gets two events to createPush, the second of which fails, leaving the// piston in a bad (simultaneously extended & not extended) state.// 4J - ignoreUpdate is a static in java, implementing as TLS here to make thread safebool PistonBaseTile::ignoreUpdate(){ return (TlsGetValue(tlsIdx) != NULL);}
void PistonBaseTile::ignoreUpdate(bool set){ TlsSetValue(tlsIdx,(LPVOID)(set?1:0));}
PistonBaseTile::PistonBaseTile(int id, bool isSticky) : Tile(id, Material::piston, isSolidRender() ){ // 4J - added initialiser ignoreUpdate(false);
this->isSticky = isSticky; setSoundType(SOUND_STONE); setDestroyTime(0.5f);
iconInside = NULL; iconBack = NULL; iconPlatform = NULL;}
Icon *PistonBaseTile::getPlatformTexture(){ return iconPlatform;}
void PistonBaseTile::updateShape(float x0, float y0, float z0, float x1, float y1, float z1){ setShape(x0, y0, z0, x1, y1, z1);}
Icon *PistonBaseTile::getTexture(int face, int data){ int facing = getFacing(data);
if (facing > 5) { return iconPlatform; }
if (face == facing) { // sorry about this mess... // when the piston is extended, either normally // or because a piston arm animation, the top // texture is the furnace bottom ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); if (isExtended(data) || tls->xx0 > 0 || tls->yy0 > 0 || tls->zz0 > 0 || tls->xx1 < 1 || tls->yy1 < 1 || tls->zz1 < 1) { return iconInside; } return iconPlatform; } if (face == Facing::OPPOSITE_FACING[facing]) { return iconBack; }
return icon;}
Icon *PistonBaseTile::getTexture(const wstring &name){ if (name.compare(EDGE_TEX) == 0) return Tile::pistonBase->icon; if (name.compare(PLATFORM_TEX) == 0) return Tile::pistonBase->iconPlatform; if (name.compare(PLATFORM_STICKY_TEX) == 0) return Tile::pistonStickyBase->iconPlatform; if (name.compare(INSIDE_TEX) == 0) return Tile::pistonBase->iconInside;
return NULL;}
//@Overridevoid PistonBaseTile::registerIcons(IconRegister *iconRegister){ icon = iconRegister->registerIcon(EDGE_TEX); iconPlatform = iconRegister->registerIcon(isSticky ? PLATFORM_STICKY_TEX : PLATFORM_TEX); iconInside = iconRegister->registerIcon(INSIDE_TEX); iconBack = iconRegister->registerIcon(BACK_TEX);}
int PistonBaseTile::getRenderShape(){ return SHAPE_PISTON_BASE;}
bool PistonBaseTile::isSolidRender(bool isServerLevel){ return false;}
bool PistonBaseTile::use(Level *level, int x, int y, int z, shared_ptr<Player> player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param{ return false;}
void PistonBaseTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr<LivingEntity> by, shared_ptr<ItemInstance> itemInstance){ int targetData = getNewFacing(level, x, y, z, dynamic_pointer_cast<Player>(by) ); level->setData(x, y, z, targetData, Tile::UPDATE_CLIENTS); if (!level->isClientSide && !ignoreUpdate()) { checkIfExtend(level, x, y, z); }}
void PistonBaseTile::neighborChanged(Level *level, int x, int y, int z, int type){ if (!level->isClientSide && !ignoreUpdate()) { checkIfExtend(level, x, y, z); }}
void PistonBaseTile::onPlace(Level *level, int x, int y, int z){ if (!level->isClientSide && level->getTileEntity(x, y, z) == NULL && !ignoreUpdate()) { checkIfExtend(level, x, y, z); }}
void PistonBaseTile::checkIfExtend(Level *level, int x, int y, int z){ int data = level->getData(x, y, z); int facing = getFacing(data);
if (facing == UNDEFINED_FACING) { return; } bool extend = getNeighborSignal(level, x, y, z, facing);
if (extend && !isExtended(data)) { if (canPush(level, x, y, z, facing)) { level->tileEvent(x, y, z, id, TRIGGER_EXTEND, facing); } } else if (!extend && isExtended(data)) { level->setData(x, y, z, facing, UPDATE_CLIENTS); level->tileEvent(x, y, z, id, TRIGGER_CONTRACT, facing); }}
/*** This method checks neighbor signals for this block and the block above,* and directly beneath. However, it avoids checking blocks that would be* pushed by this block.* * @param level* @param x* @param y* @param z* @return*/bool PistonBaseTile::getNeighborSignal(Level *level, int x, int y, int z, int facing){ // check adjacent neighbors, but not in push direction if (facing != Facing::DOWN && level->hasSignal(x, y - 1, z, Facing::DOWN)) return true; if (facing != Facing::UP && level->hasSignal(x, y + 1, z, Facing::UP)) return true; if (facing != Facing::NORTH && level->hasSignal(x, y, z - 1, Facing::NORTH)) return true; if (facing != Facing::SOUTH && level->hasSignal(x, y, z + 1, Facing::SOUTH)) return true; if (facing != Facing::EAST && level->hasSignal(x + 1, y, z, Facing::EAST)) return true; if (facing != Facing::WEST && level->hasSignal(x - 1, y, z, Facing::WEST)) return true;
// check signals above if (level->hasSignal(x, y, z, 0)) return true; if (level->hasSignal(x, y + 2, z, 1)) return true; if (level->hasSignal(x, y + 1, z - 1, 2)) return true; if (level->hasSignal(x, y + 1, z + 1, 3)) return true; if (level->hasSignal(x - 1, y + 1, z, 4)) return true; if (level->hasSignal(x + 1, y + 1, z, 5)) return true;
return false;}
bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, int facing){ ignoreUpdate(true);
if (!level->isClientSide) { bool extend = getNeighborSignal(level, x, y, z, facing);
if (extend && param1 == TRIGGER_CONTRACT) { level->setData(x, y, z, facing | EXTENDED_BIT, UPDATE_CLIENTS); return false; } else if (!extend && param1 == TRIGGER_EXTEND) { return false; } }
if (param1 == TRIGGER_EXTEND) { PIXBeginNamedEvent(0,"Create push\n"); if (createPush(level, x, y, z, facing)) { // 4J - it is (currently) critical that this setData sends data to the client, so have added a bool to the method so that it sends data even if the data was already set to the same value // as before, which was actually its behaviour until a change in 1.0.1 meant that setData only conditionally sent updates to listeners. If the data update Isn't sent, then what // can happen is: // (1) the host sends the tile event to the client // (2) the client gets the tile event, and sets the tile/data value locally. // (3) just before setting the tile/data locally, the client will put the old value in the vector of things to be restored should an update not be received back from the host // (4) we don't get any update of the tile from the host, and so the old value gets restored on the client // (5) the piston base ends up being restored to its retracted state whilst the piston arm is extended // We really need to spend some time investigating a better way for pistons to work as it all seems a bit scary how the host/client interact, but forcing this to send should at least // restore the behaviour of the pistons to something closer to what they were before the 1.0.1 update. By sending this data update, then (4) in the list above doesn't happen // because the client does actually receive an update for this tile from the host after the event has been processed on the cient. level->setData(x, y, z, facing | EXTENDED_BIT, Tile::UPDATE_CLIENTS, true); level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_TILE_PISTON_OUT, 0.5f, level->random->nextFloat() * 0.25f + 0.6f); } else { return false; } PIXEndNamedEvent(); } else if (param1 == TRIGGER_CONTRACT) { PIXBeginNamedEvent(0,"Contract phase A\n"); shared_ptr<TileEntity> prevTileEntity = level->getTileEntity(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); if (prevTileEntity != NULL && dynamic_pointer_cast<PistonPieceEntity>(prevTileEntity) != NULL) { dynamic_pointer_cast<PistonPieceEntity>(prevTileEntity)->finalTick(); }
stopSharingIfServer(level, x, y, z); // 4J added level->setTileAndData(x, y, z, Tile::pistonMovingPiece_Id, facing, Tile::UPDATE_ALL); level->setTileEntity(x, y, z, PistonMovingPiece::newMovingPieceEntity(id, facing, facing, false, true));
PIXEndNamedEvent();
// sticky movement if (isSticky) { PIXBeginNamedEvent(0,"Contract sticky phase A\n"); int twoX = x + Facing::STEP_X[facing] * 2; int twoY = y + Facing::STEP_Y[facing] * 2; int twoZ = z + Facing::STEP_Z[facing] * 2; int block = level->getTile(twoX, twoY, twoZ); int blockData = level->getData(twoX, twoY, twoZ); bool pistonPiece = false;
PIXEndNamedEvent();
if (block == Tile::pistonMovingPiece_Id) { PIXBeginNamedEvent(0,"Contract sticky phase B\n"); // the block two steps away is a moving piston block piece, so replace it with the real data, // since it's probably this piston which is changing too fast shared_ptr<TileEntity> tileEntity = level->getTileEntity(twoX, twoY, twoZ); if (tileEntity != NULL && dynamic_pointer_cast<PistonPieceEntity>(tileEntity) != NULL ) { shared_ptr<PistonPieceEntity> ppe = dynamic_pointer_cast<PistonPieceEntity>(tileEntity);
if (ppe->getFacing() == facing && ppe->isExtending()) { // force the tile to air before pushing ppe->finalTick(); block = ppe->getId(); blockData = ppe->getData(); pistonPiece = true; } } PIXEndNamedEvent(); }
PIXBeginNamedEvent(0,"Contract sticky phase C\n"); if (!pistonPiece && block > 0 && (isPushable(block, level, twoX, twoY, twoZ, false)) && (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_NORMAL || block == Tile::pistonBase_Id || block == Tile::pistonStickyBase_Id)) { stopSharingIfServer(level, twoX, twoY, twoZ); // 4J added
x += Facing::STEP_X[facing]; y += Facing::STEP_Y[facing]; z += Facing::STEP_Z[facing];
level->setTileAndData(x, y, z, Tile::pistonMovingPiece_Id, blockData, Tile::UPDATE_ALL); level->setTileEntity(x, y, z, PistonMovingPiece::newMovingPieceEntity(block, blockData, facing, false, false));
ignoreUpdate(false); level->removeTile(twoX, twoY, twoZ); ignoreUpdate(true); } else if (!pistonPiece) { stopSharingIfServer(level, x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); // 4J added ignoreUpdate(false); level->removeTile(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); ignoreUpdate(true); } PIXEndNamedEvent(); } else { stopSharingIfServer(level, x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); // 4J added ignoreUpdate(false); level->removeTile(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); ignoreUpdate(true); }
level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_TILE_PISTON_IN, 0.5f, level->random->nextFloat() * 0.15f + 0.6f); }
ignoreUpdate(false);
return true;}
void PistonBaseTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr<TileEntity> forceEntity) // 4J added forceData, forceEntity param{ int data = (forceData == -1 ) ? level->getData(x, y, z) : forceData;
if (isExtended(data)) { const float thickness = PLATFORM_THICKNESS / 16.0f; switch (getFacing(data)) { case Facing::DOWN: setShape(0, thickness, 0, 1, 1, 1); break; case Facing::UP: setShape(0, 0, 0, 1, 1 - thickness, 1); break; case Facing::NORTH: setShape(0, 0, thickness, 1, 1, 1); break; case Facing::SOUTH: setShape(0, 0, 0, 1, 1, 1 - thickness); break; case Facing::WEST: setShape(thickness, 0, 0, 1, 1, 1); break; case Facing::EAST: setShape(0, 0, 0, 1 - thickness, 1, 1); break; } } else { setShape(0, 0, 0, 1, 1, 1); }}
void PistonBaseTile::updateDefaultShape(){ setShape(0, 0, 0, 1, 1, 1);}
void PistonBaseTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr<Entity> source){ setShape(0, 0, 0, 1, 1, 1); Tile::addAABBs(level, x, y, z, box, boxes, source);}
AABB *PistonBaseTile::getAABB(Level *level, int x, int y, int z){ updateShape(level, x, y, z); return Tile::getAABB(level, x, y, z);}
bool PistonBaseTile::isCubeShaped(){ return false;}
int PistonBaseTile::getFacing(int data){ return data & 0x7;}
bool PistonBaseTile::isExtended(int data){ return (data & EXTENDED_BIT) != 0;}
int PistonBaseTile::getNewFacing(Level *level, int x, int y, int z, shared_ptr<LivingEntity> player){ if (Mth::abs((float) player->x - x) < 2 && Mth::abs((float) player->z - z) < 2) { // If the player is above the block, the slot is on the top double py = player->y + 1.82 - player->heightOffset; if (py - y > 2) { return Facing::UP; } // If the player is below the block, the slot is on the bottom if (y - py > 0) { return Facing::DOWN; } } // The slot is on the side int i = Mth::floor(player->yRot * 4.0f / 360.0f + 0.5) & 0x3; if (i == 0) return Facing::NORTH; if (i == 1) return Facing::EAST; if (i == 2) return Facing::SOUTH; if (i == 3) return Facing::WEST; return 0;}
bool PistonBaseTile::isPushable(int block, Level *level, int cx, int cy, int cz, bool allowDestroyable){ // special case for obsidian if (block == Tile::obsidian_Id) { return false; }
if (block == Tile::pistonBase_Id || block == Tile::pistonStickyBase_Id) { // special case for piston bases if (isExtended(level->getData(cx, cy, cz))) { return false; } } else { if (Tile::tiles[block]->getDestroySpeed(level, cx, cy, cz) == Tile::INDESTRUCTIBLE_DESTROY_TIME) { return false; }
if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_BLOCK) { return false; }
if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_DESTROY) { if(!allowDestroyable) { return false; } return true; } }
if( Tile::tiles[block]->isEntityTile() ) // 4J - java uses instanceof EntityTile here { // may not push tile entities return false; }
return true;}
bool PistonBaseTile::canPush(Level *level, int sx, int sy, int sz, int facing){ int cx = sx + Facing::STEP_X[facing]; int cy = sy + Facing::STEP_Y[facing]; int cz = sz + Facing::STEP_Z[facing];
for (int i = 0; i < MAX_PUSH_DEPTH + 1; i++) {
if (cy <= 0 || cy >= (Level::maxBuildHeight - 1)) { // out of bounds return false; }
// 4J - added to also check for out of bounds in x/z for our finite world int minXZ = - (level->dimension->getXZSize() * 16 ) / 2; int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1; if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) ) { return false; } int block = level->getTile(cx, cy, cz); if (block == 0) { break; }
if (!isPushable(block, level, cx, cy, cz, true)) { return false; }
if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_DESTROY) { break; }
if (i == MAX_PUSH_DEPTH) { // we've reached the maximum push depth // without finding air or a breakable block return false; }
cx += Facing::STEP_X[facing]; cy += Facing::STEP_Y[facing]; cz += Facing::STEP_Z[facing]; }
return true;
}
void PistonBaseTile::stopSharingIfServer(Level *level, int x, int y, int z){ if( !level->isClientSide ) { MultiPlayerLevel *clientLevel = Minecraft::GetInstance()->getLevel(level->dimension->id); if( clientLevel ) { LevelChunk *lc = clientLevel->getChunkAt( x, z ); lc->stopSharingTilesAndData(); } }}
bool PistonBaseTile::createPush(Level *level, int sx, int sy, int sz, int facing){ int cx = sx + Facing::STEP_X[facing]; int cy = sy + Facing::STEP_Y[facing]; int cz = sz + Facing::STEP_Z[facing];
for (int i = 0; i < MAX_PUSH_DEPTH + 1; i++) { if (cy <= 0 || cy >= (Level::maxBuildHeight - 1)) { // out of bounds return false; }
// 4J - added to also check for out of bounds in x/z for our finite world int minXZ = - (level->dimension->getXZSize() * 16 ) / 2; int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1; if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) ) { return false; }
int block = level->getTile(cx, cy, cz); if (block == 0) { break; }
if (!isPushable(block, level, cx, cy, cz, true)) { return false; }
if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_DESTROY) { // this block is destroyed when pushed Tile::tiles[block]->spawnResources(level, cx, cy, cz, level->getData(cx, cy, cz), 0); // setting the tile to air is actually superflous, but helps vs multiplayer problems stopSharingIfServer(level, cx, cy, cz); // 4J added level->removeTile(cx, cy, cz); break; }
if (i == MAX_PUSH_DEPTH) { // we've reached the maximum push depth without finding air or a breakable block return false; }
cx += Facing::STEP_X[facing]; cy += Facing::STEP_Y[facing]; cz += Facing::STEP_Z[facing]; }
int ex = cx; int ey = cy; int ez = cz; int count = 0; int tiles[MAX_PUSH_DEPTH + 1];
while (cx != sx || cy != sy || cz != sz) {
int nx = cx - Facing::STEP_X[facing]; int ny = cy - Facing::STEP_Y[facing]; int nz = cz - Facing::STEP_Z[facing];
int block = level->getTile(nx, ny, nz); int data = level->getData(nx, ny, nz);
stopSharingIfServer(level, cx, cy, cz); // 4J added
if (block == id && nx == sx && ny == sy && nz == sz) { level->setTileAndData(cx, cy, cz, Tile::pistonMovingPiece_Id, facing | (isSticky ? PistonExtensionTile::STICKY_BIT : 0), Tile::UPDATE_NONE); level->setTileEntity(cx, cy, cz, PistonMovingPiece::newMovingPieceEntity(Tile::pistonExtensionPiece_Id, facing | (isSticky ? PistonExtensionTile::STICKY_BIT : 0), facing, true, false)); } else { level->setTileAndData(cx, cy, cz, Tile::pistonMovingPiece_Id, data, Tile::UPDATE_NONE); level->setTileEntity(cx, cy, cz, PistonMovingPiece::newMovingPieceEntity(block, data, facing, true, false)); } tiles[count++] = block;
cx = nx; cy = ny; cz = nz; }
cx = ex; cy = ey; cz = ez; count = 0;
while (cx != sx || cy != sy || cz != sz) { int nx = cx - Facing::STEP_X[facing]; int ny = cy - Facing::STEP_Y[facing]; int nz = cz - Facing::STEP_Z[facing];
level->updateNeighborsAt(nx, ny, nz, tiles[count++]);
cx = nx; cy = ny; cz = nz; }
return true;
}