#how to use getBlock to detect specific block around player
1 messages ยท Page 1 of 1 (latest)
like at 7 blocks or 7 blocks around the player
7 blocks around player
ah alr
I would say use for loops, but someone told me to do that one time, and it was very laggy
how?
how can i make it only detect in 7 blocks around player
for (var i = 0; i < player.location; i++) {
world.getDimension.getBlock({x: block.location[i] <= 7, y: block.location[i] <= 7, z: block.location[i] <= 7})
}``` @magic ferry will this work?
how to use getBlock to detect specific block around player
I would honestly make it start at 7, and then subtract the value by 1 using i, and then have it reset once it gets to the players location
also, I wouldn't have <= 7 inside of the part where you're trying to get the blocks location
I'm not sure if that will work or not...
also, I would make "var", to "let" instead. From what I've seen, and from what I've done, I would use that instead...
What about the BlockVolume class with the getBlockLocationIterator() method? This could be faster. I have not tried it myself yet though.
not in stable tho
Oh I thought I was looking at latest stable ๐ตโ๐ซ sorry!
you don't need to be sorry
Do you just need the specific for loop that would test all the blocks? Gimme a bit and I'll grab my PC and do it.
Ok working on it. Am assuming 7 blocks around player means a cube 15 blocks wide centred on the player. (That's 3375 calls to getBlock.)
But if the cube is smaller, the algorithm is the same.
Assuming here you want to match type, and just want a true/false result for the presence of the type, rather than a list of the block locations that match the type.
Aight I'm back. Got some code.
function isBlockTypeNearPlayer(player, blockTypeId, radius) {
try {
const radiusInt = Math.floor(radius);
const dimension = player.dimension
const x0 = Math.floor(player.location.x) - radiusInt;
const y0 = Math.floor(player.location.y) - radiusInt;
const z0 = Math.floor(player.location.z) - radiusInt;
const xMax = x0 + radiusInt*2;
const yMax = y0 + radiusInt*2;
const zMax = z0 + radiusInt*2;
for (let x = x0; x <= xMax; x++) {
for (let y = y0; y <= yMax; y++) {
for (let z = z0; z <= zMax; z++) {
if (blockTypeId === dimension.getBlock({x: x, y: y, z: z}).typeId) return true;
}
}
}
return false;
} catch (e) { world.sendMessage(`Error caught in isBlockTypeNearPlayer(): ${e.message}`)}
}
If radius is 7, then it will search 15 x 15 x 15 blocks centred around the player, including the player's block.
Didn't sanity check radius but can do that. I did floor it to be an integer, as well as location, though the latter isn't strictly necessary.
If the function encounters a matching block Id, it will quit and return true immediately. If it never does, it will return false.
(It's not exactly a "radius" like a circle but you know...)
would this work in stable if just change the typeId logic to permutation?
also mb, im just interested on how to do this myself.
This does cause a watchdog spike at the large volume it is searching in. Might have to parcel it up into jobs otherwise
Don't know, hmmm
it should right
like im looking at it
i see no beta things except block typeId
I don't usually code in stable so I don't pay attention enough, I'm having a look at the docs now
maybe ill just try it and ill let you know
Oh is that beta? Oops!
block type Id isnt out on stable yet, yeah
function isBlockTypeNearPlayer(player, blockTypeId, radius) {
try {
const radiusInt = Math.floor(radius);
const dimension = player.dimension
const x0 = Math.floor(player.location.x) - radiusInt;
const y0 = Math.floor(player.location.y) - radiusInt;
const z0 = Math.floor(player.location.z) - radiusInt;
const xMax = x0 + radiusInt * 2;
const yMax = y0 + radiusInt * 2;
const zMax = z0 + radiusInt * 2;
for (let x = x0; x <= xMax; x++) {
for (let y = y0; y <= yMax; y++) {
for (let z = z0; z <= zMax; z++) {
if (dimension.getBlock({ x: x, y: y, z: z }).permutation.matches(blockTypeId)) return true;
}
}
}
return false;
} catch (e) { world.sendMessage(`Error caught in isBlockTypeNearPlayer(): ${e.message}`) }
}
yeah, thankfully the only thing that was beta was just that
thank you, alot.
Oh that's better. Yeah I see matches is in pre-release for Block too.
Hopefully the iterator will be stable soon. I haven't looked at Mojang's leveldb spec but surely they would have made it more efficient than them having to access each block slot each time - because statistically blocks of the same type are likely to be next to one another. Like with gif formats and so forth, you could "compress" or speed up access by specifying how many contiguous blocks are of that type. It's horribly inefficient having to access each one, one by one, in a relatively small volume.
Oh, umm, if the iterator does become stable soon, that would be a better method.
Probably.
Okay
this is still cool
Cheers, hope it's useful!
but yes, performance is everything
@urban star why does it gives error: can not read property x of undefined?
@elfin pecan ^
I didn't get that error.
can you show me how did you used that function?
have you modified it all
nah i haven't modified just used what you made changes
treat this function like how you would treat methods.
system.runInterval(() => {
let mobSpawnerDestroyChance = Math.random()*100
if (isBlockTypeNearPlayer(world.getPlayers(), 'minecraft:mob_spawner', 7))
if (mobSpawnerDestroyChance <= 50) {
setBlockTo(world.getPlayers(), 'minecraft:air', 7)
}
}, 20);```
is anything wrong here?
looks like you need to learn what for of loops are
because that wont work like that.
then how ima supposed to do that?
The for...of statement executes a loop that operates on a sequence of values sourced from an iterable object. Iterable objects include instances of built-ins such as Array, String, TypedArray, Map, Set, NodeList (and other DOM collections), as well as the arguments object, generators produced by generator functions, and user-defined iterables.
you should use the world.getAllPlayers() method
it returns an array of all players in the world.
knowing that, you should understand what you need to do by looking at this doc.
also you are making 2 if statements which can be turned into 1 with the && operator
if (something1 === whatever) {
if (something2 === whatever) {
//...
}
}```
you should do this instead...
```js
if (something1 === whatever && something2 === whatever) {
//...
}
how dumb i am i just forgot that i had to do for of loop first ๐ (brain isn't braining moment)
Thanks to you all
Will this work if I'm on 1.10.0-beta?
Yep. That's what my default test environment is and it worked in that.
Hang on, just saw, did you fix this error?
I'm assuming there was some issue with player.location.x because player parameter passed into the function wasn't a valid player object. Need to get that first.
If anyone is here just learning scripting, I tested it by moving around in the world and issuing the dummy command "/scriptevent fudj:fdsf fdsdfd" (can put anything as long as there's a colon in the middle of the first string) which then called the API callback for scriptevent, which then called an interim function which called the function I gave above, but with player[0] and a hard coded block type ID and a radius number, eg "minecraft:air" and 7. I also did some world.sendMesaage debugs of the variables to screen.
I haven't looked into the gametest framework yet, and really should use VSCode and debugging more. (My code editor of habit is Notepad++)
Idk, is there much on this discord about how people test/debug their code? If there isn't, that would be a helpful thing to talk more about. I just noticed jayly's script debugger here and should try it, you should too if you haven't!
(I used to teach coding a long time ago but I'm a bit rusty at how to explain stuff and tl;dr a lot.)
Should this code snippet be put on dev resources like suggested, if it helps more people? It seems like it will be outdated soon, and it is inefficient unless you absolutely need it, but can be. Does a mod have to approve it first?
I did think of a way of making this more efficient, if the block type to search for is likely to be several blocks together, such as ore or lava. Not a single placed block.
Because blocks of the same type are more likely to be contiguous, it would be better to search in a spaced out way then zoom in. Once you find the block you can stop searching, so this is a way to detect its presence sooner.
Of course if no block is present, then it will take the full amount of time to search.
it was because i forgot to do for of loop to world.getAllPlayers()
its working fine
Excellent!
but massive spikes
Yeah I can imagine.
what if you add the checking operation to an async pool of commands? that way the bottom of the queue is bumped up after only a little while but still doesn't lag the game out (343 operations isn't that much but checking every tick i would imagine could be resource intensive)
Are you dealing with lots of players? Does it have to be executed continuously or can it be spaced out over a few seconds?
Yeah I'm thinking about runJob type thing but it depends on what the use case is.
yeah if it absolutely has to check all blocks every tick this seems like the only way
still o(n) right lol ยฏ_(ใ)_/ยฏ
I'm embarrassed to say I vagued out during that section of the course, so I'm not great at big O and have really been meaning to rectify that. Here, the complexity is what I think would be O(n^3) where n is the distance or "radius" in blocks from you to search. It's not exponential at least but it's not great.
I am pretty sure Mojang's iterator would take shortcuts like I described. I am going to benchmark that but not right now.
i think here n would refer to the number of blocks
ig it depends since the radius is the input
Yes, but if the desired input is radius, then the complexity added just by adding an extra block away is soooo much more, and gets worse the further away you get.
Yep.
Mojang have made their leveldb implementation (how they store block data for the world etc) open source, probably because it's based on open source google format, I am now really curious how it's done and how it might speed up access by "compression" because contiguous blocks are more likely to be the same.
Or maybe it's crap idk. It's the bedrock variety, which was probably designed for smallness and speed. I think the Java way of storing world data is slower but more flexible, and most probably makes floatater more possible.
Nice thanks
^ @lyric portal Do you need to continuously check every tick or is this a more occasional check? Spikes can be prevented if check loop work is able to be spread out over several ticks rather than all in one tick which triggers the watchdog, though that of course would mean introducing a small delay in response (though spikes are a kind of delay too).
Unless the player is going super fast or the blocks around them are changing super fast, I don't think it would be necessary to check every tick. Maybe even every 20 ticks (1 second). Don't know your particular use case, but can come up with some code to spread across several ticks to hopefully eliminate spikes.
its like if player is near mob spawner for around 5 or 6mins it will break the mob spawner if you know how i can spread loops tell me
system.runJob() isn't stable yet but I've done it another way before so can be done as in running a certain amount of loops per tick, stopping and saving the loop progress for a player in a kind of queue, and getting system.runInterval() to check the queue every tick and execute another parcel of that loop.
So just the ability to stop and continue the algorithm above, storing that player location and other progress in a queue for runInterval to check.
idk what is runJob but when it will be stable i will probably @ you for the help
And handling multiple players.
runJob basically handles the stop, save progress, now continue stuff without having to code all of it yourself. https://learn.microsoft.com/en-us/minecraft/creator/scriptapi/minecraft/server/system?view=minecraft-bedrock-stable#runjob
A "job" in computer speak can be defined as a "parcel" of processing to be done, that can be put on hold, and scheduled to run when convenient.
I still don't get how system.runJob do or works
system.runJob(() => {
//code here
});```
same ๐
Have you tried running the sample code?
Also worth looking at general info about what generator functions are, and the yield keyword in javascript.
That said, I haven't tried it myself yet, so maybe it just doesn't work.
Nah
But I'm gonna try your code with system.runJob
Is this valid?
function isBlockTypeNearPlayer(player, blockTypeId, radius) {
try {
const radiusInt = Math.floor(radius);
const dimension = player.dimension
const x0 = Math.floor(player.location.x) - radiusInt;
const y0 = Math.floor(player.location.y) - radiusInt;
const z0 = Math.floor(player.location.z) - radiusInt;
const xMax = x0 + radiusInt*2;
const yMax = y0 + radiusInt*2;
const zMax = z0 + radiusInt*2;
for (let x = x0; x <= xMax; x++) {
for (let y = y0; y <= yMax; y++) {
for (let z = z0; z <= zMax; z++) {
if (blockTypeId === dimension.getBlock({x: x, y: y, z: z}).typeId) return true;
}
}
}
return false;
} catch (e) { world.sendMessage(`Error caught in isBlockTypeNearPlayer(): ${e.message}`)}
}
system.runJob(() => {
for (const player of world.getPlayers()) {
isBlockTypeNearPlayer(player, 'minecraft:crafting_table', 7)
}
});
You'll need a yield in there at regular intervals in the loop or else it won't yield control back and get paused, ready to run some more of the loop the next tick. See the example code.
Clear the job if find a matching block type and want to shortcut the loop.
Example code is in the documentation link above. It's in typescript so needs to be converted to javascript (take out any variable types)
How many cycles of the loop per tick before yielding really depends on tolerance for lag and how long the entire job could take. If you needed to run 3375 max iterations (because radius is 7 blocks), but you didn't want it to take more than a second/20 ticks to return a result, you'd want to yield after running about (3375/20) iterations of the loop each time. Say, about 170 iterations then yield. Could run a counter and test, though this is introducing more processing per iteration. Or could just yield after every run through the second innermost loop, which will run 15 x 15 = 225 times each time the outermost loop is incremented. It really depends on the use case.
My head hurts reading alot of this pls help ๐ญ
Sorry, I have a terrible habit of typing a lot, I am hyperlexic, find it hard to watch video without subtitles though!
I just had a look at the example code again and now I am cross because I thought I had been told the "time slice" was once per tick, but now I'm not so sure.
Here.
I'm not near my PC or else I would try this.
function isBlockTypeNearPlayer(player, blockTypeId, radius) {
try {
const radiusInt = Math.floor(radius);
const dimension = player.dimension
const x0 = Math.floor(player.location.x) - radiusInt;
const y0 = Math.floor(player.location.y) - radiusInt;
const z0 = Math.floor(player.location.z) - radiusInt;
const xMax = x0 + radiusInt*2;
const yMax = y0 + radiusInt*2;
const zMax = z0 + radiusInt*2;
for (let x = x0; x <= xMax; x++) {
for (let y = y0; y <= yMax; y++) {
for (let z = z0; z <= zMax; z++) {
if (blockTypeId === dimension.getBlock({x: x, y: y, z: z}).typeId) {
return true;
return { x: x, y: y, z: z };
}
}
}
}
return false;
return { x: undefined, y: undefined, z: undefined };
} catch (e) {}
}
system.runInterval(() => {
for (const player of world.getPlayers()) {
const block = isBlockTypeNearPlayer(player, "minecraft:crafting_table", 2);
if (block) {
player.sendMessage(`${block.x} ${block.y} ${block.z}`);
}
}
});
I modified the code where I could retrieve the coordinates. Is this valid @urban star
Sorry for ping
Just reading the code...
Can't return "true" and an object at same time. Best just to either return a location object or null.
If returned block result is valid, it found one at that location (though there may be other blocks of that type that weren't found yet). If it's null, the entire volume was searched and didn't find one.
^ ping
Hang on, is BlockVolume and the iterator in preview or beta in latest stable?
Beta
Ok better go so I hopefully have time later to try this and runJob out on my pc
I know now
Here look
function findBlockNearPlayer(player, blockTypeId, radius) {
try {
const radiusInt = Math.floor(radius);
const dimension = player.dimension;
const x0 = Math.floor(player.location.x) - radiusInt;
const y0 = Math.floor(player.location.y) - radiusInt;
const z0 = Math.floor(player.location.z) - radiusInt;
const xMax = x0 + radiusInt*2;
const yMax = y0 + radiusInt*2;
const zMax = z0 + radiusInt*2;
for (let x = x0; x <= xMax; x++) {
for (let y = y0; y <= yMax; y++) {
for (let z = z0; z <= zMax; z++) {
const block = dimension.getBlock({ x: x, y: y, z: z });
if (block.typeId === blockTypeId) {
return { x: x, y: y, z: z };
}
}
}
}
// If no block is found
return null;
} catch (e) {
return null;
}
}
system.runInterval(() => {
for (const player of world.getPlayers()) {
const blockLocation = findBlockNearPlayer(player, "minecraft:crafting_table", 2);
if (blockLocation) {
const { x, y, z } = blockLocation;
player.sendMessage(`Crafting table found at: ${x}, ${y}, ${z}`);
}
}
});```
Wait I'm not really sure cause I don't use that often
Looks like it https://jaylydev.github.io/scriptapi-docs/latest/classes/_minecraft_server_1_10_0_beta.BlockVolume.html
Documentation for Script API - v1.20.70
I successfully did it
Las problem dude
It can only check 1 block per radius
How to get multiple
Nvm just discovered how
function findAllBlocksNearPlayer(player, blockTypeId, radius) {
try {
const radiusInt = Math.floor(radius);
const dimension = player.dimension;
const blockLocations = [];
const x0 = Math.floor(player.location.x) - radiusInt;
const y0 = Math.floor(player.location.y) - radiusInt;
const z0 = Math.floor(player.location.z) - radiusInt;
const xMax = x0 + radiusInt*2;
const yMax = y0 + radiusInt*2;
const zMax = z0 + radiusInt*2;
for (let x = x0; x <= xMax; x++) {
for (let y = y0; y <= yMax; y++) {
for (let z = z0; z <= zMax; z++) {
const block = dimension.getBlock({ x: x, y: y, z: z });
if (block.typeId === blockTypeId) {
blockLocations.push({ x: x, y: y, z: z });
}
}
}
}
return blockLocations;
} catch (e) {
return [];
}
}```
I need to store them on an arra
Yah, looks good to me
Trying runJob out now
Oh cool it worked. Interesting, the job was run several times in the same tick.
I tried bigger and bigger values for radius, yielding after every run through the y loop. The more work the function has to do before yielding, the less likely it will be to be run by the System more than once per tick. I finally tried radius of 20; it ran every tick for 41 ticks, and gave a watchdog warning of 6ms average for slow running script.
Apologies if what I'm discussing here isn't clear or immediately relevant, but I'm not sure how much runJob has been talked about in Bedrock Add-Ons yet, so I think it's useful for me to talk about how it is working when tested.
Perhaps it would be easier if I just post my script here. Obviously this is 1.10.0-beta for @minecraft/server
This is just for debugging/testing purposes if anyone else wants to try this out.
It tests every block in the cube, it doesn't return after it finds a matching block. I'll try that next.
...okay, pretty sure I have to mess around with Promises in order to get the search result only after the function has completed, as function is now asynchronous with runJob. runJob itself delivers its Promise after the first tick, which isn't what I want. If anyone else is experienced with this kind of thing, please chime in, I'm going to sleep on it.
Hey bro can you help how to fix the lag? Cause when I put a high value it lag so bad.
function findAllBlocksNearPlayer(player, blockTypeId, radius) {
try {
const radiusInt = Math.floor(radius);
const dimension = player.dimension;
const blockLocations = [];
const x0 = Math.floor(player.location.x) - radiusInt;
const y0 = Math.floor(player.location.y) - radiusInt;
const z0 = Math.floor(player.location.z) - radiusInt;
const xMax = x0 + radiusInt*2;
const yMax = y0 + radiusInt*2;
const zMax = z0 + radiusInt*2;
for (let x = x0; x <= xMax; x++) {
for (let y = y0; y <= yMax; y++) {
for (let z = z0; z <= zMax; z++) {
const block = dimension.getBlock({ x: x, y: y, z: z });
if (block.typeId === blockTypeId) {
blockLocations.push({ x: x, y: y, z: z });
}
}
}
}
return blockLocations;
} catch (e) {
return [];
}
}
system.runInterval(() => {
for (const player of world.getPlayers()) {
const blockLocations = findAllBlocksNearPlayer(player, "minecraft:crafting_table", 7);
for (const blockLocation of blockLocations) {
const { x, y, z } = blockLocation;
player.runCommand(`summon fireworks_rocket ${x} ${y} ${z}`);
}
}
});```
Pls help
From what I can see you are running findAllBlocksNearPlayer() every tick for every player. With 5 players, you'd be attempting to run the function 100 times a second, except it wouldn't complete in that time and so lag to try to complete it. The function that is being run 100 times a second (with 5 players) would loop 3375 times each time it is run, as the radius is 7 and you're searching a cube of 15 x 15 x 15 blocks = 3375 blocks. So you're attempting to search 3375 x 20 = 67500 blocks a second per player, so if that was 5 players it would be 3375 x 100 = 337500 blocks searched in (an attempted) per second.
Sorry, wall of text again!
Soooooo......
Plus, then you run a command on the results, 20 times a second per player, for as many blocks as you find for each player. If each player of 5 players had an average of 2 blocks found, then you'd be trying to execute a command 20 x 5 x 2 times a second on top of running the search function, and commands are slow. With visual results to calculate, probably slower.
No problem, I like how you're direc to the point
Yes even tho the value is high
I want to make a ticking for a block but you know you can't put ticking on a vanilla vlock
- You can reduce the frequency of time you search for blocks. Players usually won't move very far. A second or less time. So every 20 ticks or more for runInterval
- Do you need to search a cube? Will the blocks you're looking for likely to be in all directions above and below? If you can get the volume you're searching in smaller, that would help.
Here Imma explain what I'm trying to do
A radius of 5 vs 7 would make a big difference on performance. 5 means searching a cube 11 x 11 x 11 which is 1331 which is way less than 3375, 40% of the search time.
- Are players likely to be close to one another sometimes. You could optimise by combining the search locations found so any that are the same location get a command only run once on them.
So I have a custom block, my block can only be placed sides so I don't need to problem up and down, I wanna make a tick but I won't use the ticking cause I want my add-on works on 1.20.70 and above not just 1.20.70. I need to get the location of the block so I could change the permutation of that block every tick.
That's why I'm gonna use the radius thingy cause maybe it's a good alternative way
Can you maintain a global list of where all these custom blocks are placed per dimension, and update those that are in a loaded chunk? That way you don't need to keep continuously checking. You'd have to detect with event when one was placed by the player or whatever mechanism and update.
Also I got an idea where I could just detect I place the block and store the location of the block but It won't work if the player didn't place that
Cause place block works on players only
Maybe there's another way, I'm not sure. But blocks can have animated textures can't they? Not sure if that's what you want though.
Yes but I don't want the block to be animated on my hotbar ๐
And I'm lazy to texture a bunch
Yeah, it's a problem if that block is placed in some other ways where it's harder to detect. I guess at least though you could run a continuous check in a loaded chunk near players, but making sure you don't check same location more than once every X seconds or something, for efficiency.
I think you can have different textures for blocks in hotbar? Or is that only items?
Hmm I feel you can have diff textures in hotbar. And at least there used to be the option to turn off animation in hotbar though maybe only for items not blocks.
If the block had animated texture it will be animated on the hotbar too
Oh okay. Hmm.
The items are the only one you can't animate
Wait if I make the block cannot be accessed by command and the block can only be placed by item does it detect I place it?
Hopefully? But if endermen can move it around or it gets blown up or whatever that could complicate things, if mob griefing is turned on.
Looks like there's no other way, I can only animate it by textures
It would be way simpler and less processor intensive ๐คท
Yeah haha
Hopefully lots more dynamic block stuff coming. I really want the Floatater type capability to push blocks around! (Java has more dynamic way of treating blocks, kinda like entities.)
Also I want to use the "ticking" or hard way cause I wanna a make the player can choose the speed of the animation
Yeah there's so much that could be done with that. All kinds of properties could be altered.
Make the default texture not animated, alr. Then make a block property, and make it a bool, and make it false by default. Then, use permutations, in order to detect the value of the property made. If it's false, make the texture for the block the non-animated one. And if it's true, use the animated one. For switching the textures, make it to where if you place the block, it changes the property to true. You can use do that using scripts.