connection checker: What am I doing wrong? / is this a bug in blockly?

199 views
Skip to first unread message

frm...@gmail.com

unread,
Jul 26, 2022, 4:31:06 PM7/26/22
to Blockly
Hi all,

For a somewhat extreme Blockly project am trying to add a connection checker for statements. The goal is to have the container_block to check if the type of the statement_block will satisfy some constraint and if so to allow the connection. The code works fine without the connection checker. Adding a connection checker works fine in the sense that the UI allows the container block to exclusively connect with a statement_block with a type that matches the container. Great. Only problem is the generator. The container block generator returns null for getInputTargetBlock once the check field is added to the block description (see red addition bellow)).

(Clarifying: the new connector checker is less restrictive than the default connector checker, maybe the connector checker is not called when generating code???)

Example #1. (code)
Generator works fine, but no type checking used.

container_block
{
"type": "container_block",
...
"args0": [
{
"type": "input_statement",
"name": "content"
}
],
...

statement_block
{
"type": "statement_block",
...
"previousStatement": 'statement_block_connector',
"nextStatement": 'statement_block_connector',
...
}


statement_block generator
....
let currentBlock = this.getInputTargetBlock('content');
console.log("Generator message BLOCK: ", currentBlock);
...

Checker
export class NewBlockChecker extends Blockly.ConnectionChecker {
....
/**
* @override
*/
doTypeChecks(a: any, b: any) {
const checkArray0 = a.getCheck();
const checkArray1 = b.getCheck();

if ((checkArray0[0] === 'STATEMENT_CONNECTOR") &&
(checkArray1[0] !== "statement_block_connector"))
  return false;
if ((checkArray1[0] === 'STATEMENT_CONNECTOR") &&
(checkArray0[0] !== "statement_block_connector"))
  return false;
  return true;
}

Example #1 (result)
Generator message BLOCK: Blockly.Block {id: 'm%%]x5.Nv|^|X10R9GRa',...

Example #2. (code)
Type checking works fine, but the generator does not.
Only difference is the lines added in red, rest code is kept the same 

revised container_block
{
"type": "container_block",
...
"args0": [
{
"type": "input_statement",
"name": "content",
"check": [
"STATEMENT_CONNECTOR"
]

}
],
...

Example #2 (result)

The checker works correctly yet:
Generator message BLOCK: null

Cheers,
Oded



Maribeth Bottorff

unread,
Jul 26, 2022, 5:09:33 PM7/26/22
to Blockly
The connection checker isn't checked when generating code. `getInputTargetBlock` just looks at connections that are already connected on the block; the connection checker would have already checked the potential connection earlier, there is no need to involve the connection checker at code generation time. In other words, if a connection isn't allowed, then the blocks already would not be connected by the time we are generating code.

Are you sure your custom connection checker is actually being used? Where are you registering it? The fact that block is null tells me the connection isn't being made, are you sure they are connected on the workspace (visually)? If the default connection checker were being used, then this is the behavior I would expect (the default checker would not allow this connection and therefore the target block would be null)

As a side note, I don't think you need a custom connection checker to get this behavior. Just set the "check" value of the input statement to an array containing the same value as the "previous_statement". So take your first code example but add "check": ["statement_block_connector"] to the container block and it should work out of the box. You might already know this and this is just a simplified example but thought I'd mention it just in case.

Maribeth

frm...@gmail.com

unread,
Jul 26, 2022, 6:01:41 PM7/26/22
to Blockly
Thanks for your swift response Maribeth.

Are you sure your custom connection checker is actually being used?
100% certain the checker is being used, since I am logging from within it debug messages.

The registration code is:

export const registrationType: any = Blockly.registry.Type.CONNECTION_CHECKER;
export const registrationName = 'NewBlockChecker';

// Register the checker so that it can be used by name.
Blockly.registry.register(
registrationType, registrationName, ArenaBlockChecker);

export const pluginInfo = {
[registrationType]: registrationName,
....

are you sure they are connected on the workspace (visually)?
It is visually connected :)
also when removing the check from the container block the generator works correctly.
the connection is done programmatically, but just in case did connect / disconnect manually and the same behavior persists.


As a side note, I don't think you need a custom connection checker to get this behavior. Just set the "check" value of the input statement to an array containing the same value as the "previous_statement". So take your first code example but add "check": ["statement_block_connector"] to the container block and it should work out of the box. You might already know this and this is just a simplified example but thought I'd mention it just in case.

Thanks for the suggestion but I am working in a setting where multiple block types are created dynamically,  indeed, this is a simplified example.

Maribeth Bottorff

unread,
Jul 26, 2022, 7:30:18 PM7/26/22
to Blockly
Okay I tried to reproduce this and I wasn't able to, but I had to make some changes to the code you showed here. I assume some of this is just due to the way you've simplified the code for the sake of example but these are areas to double check.

- The registration code you mentioned references 'ArenaBlockChecker' but the class you provided is called NewBlockChecker.
- The generator code you provided says it's for statement_block, but the statement_block doesn't have an input called 'content'. That code needs to be in the generator for container_block.
- The doTypeCheck method you provided throws an error for any connection where one of the connection points doesn't have any checks listed, or the check is null. So for example in your original sample code, if you don't have a check array on the "content" input, then the connection checker will throw an error because b.getCheck() will return null, and you'll get an error when accessing `checkArray1[0]`. So your doTypeChecks method doesn't work as written, and if this is the verbatim method you have in the connection checker and you don't have any early returns in case getCheck is null, then I am not sure how your first example code could be working.
- There is a syntax error in doTypeCheck where you open a string with a single quote but close it with a double quote.

After fixing these problems, I'm not able to reproduce the issue. Inside the code generator the target block is defined if there is indeed a block connected in there, and is null if not. So I think there is probably an error in your connection checker, or the way that you've registered it, and not in Blockly. If you post your full non-simplified checker that may help us find it together.

For reference here is how I changed the connection checker code:

doTypeChecks(a, b) {
const checkArray0 = a.getCheck();
const checkArray1 = b.getCheck();

if (!checkArray0 || !checkArray1) {
  // Strict check. Return true if you want to allow connections where one or both blocks have not specified any checks.
  return false;
}

if ((checkArray0[0] === 'STATEMENT_CONNECTOR') && (checkArray1[0] !== 'statement_block_connector')) {
  return false;
}
if ((checkArray1[0] === 'STATEMENT_CONNECTOR') && (checkArray0[0] !== 'statement_block_connector')) {
  return false;
}
return true;
}

Hope that helps,

Maribeth

frm...@gmail.com

unread,
Jul 26, 2022, 11:45:39 PM7/26/22
to Blockly
My bad for being so sloppy!
But still does not work :(. Copying real code this time, learned my lesson.
Changed your checker to return true in case any of the blocks has not specified any checks.

Example #1

BLOCKS 

{
"type": "container_block_implementation",
"message0": " Name %1",

"args0": [
{
"type": "input_statement",
"name": "content"
}
],
"inputsInline": true,
"colour": "EXAMPLES"
},
{
"type": "statement_block",
"inputsInline": true,
"message0": "Statement Block",

"previousStatement": "statement_block_connector",
"nextStatement": "statement_block_connector",
"colour": "EXAMPLES"
},


Generators

Blockly.JavaScript['statement_block'] = function (block) {
return '/**/';
}

Blockly.JavaScript['container_block_implementation'] = function (block) {

let currentBlock = this.getInputTargetBlock('content');
console.log("container_block_implementation currentBlock:", currentBlock);
...

}


Checker + registration

export class ArenaBlockChecker extends Blockly.ConnectionChecker {
/**
* Constructor for the connection checker.
*/
constructor() {
super();

}

/**
* @override
*/
doTypeChecks(a: any, b: any) {
const checkArray0 = a.getCheck();
const checkArray1 = b.getCheck();
console.log("Checker arrays:", checkArray0, checkArray1);

if (!checkArray0 || !checkArray1) {
// Strict check. Return true if you want to allow connections where one or both blocks have not specified any checks.
return true;

}
if ((checkArray0[0] === 'STATEMENT_CONNECTOR') && (checkArray1[0] !== 'statement_block_connector')) {
return false;
}
if ((checkArray1[0] === 'STATEMENT_CONNECTOR') && (checkArray0[0] !== 'statement_block_connector')) {
return false;
}
console.log("Blocks are connected!!!");
return true;
}

export const registrationType: any = Blockly.registry.Type.CONNECTION_CHECKER;
export const registrationName = 'ArenaBlockChecker';


// Register the checker so that it can be used by name.
Blockly.registry.register(
registrationType, registrationName, ArenaBlockChecker);

export const pluginInfo = {
[registrationType]: registrationName,
};

Example #1 (result)
Checker arrays: ['statement_block_connector'] null
Blocks are connected!!!
container_block_implementation currentBlock: Blockly.Block {id: 'pC8DDN)!sW-4EMb]N,%E',...   // <--- this is good, though checker not working :)

Example #2 (code)
{
"type": "container_block_implementation",
"message0": " Name %1",

"args0": [
{
"type": "input_statement",
"name": "content",
"check": [
"STATEMENT_CONNECTOR"
]

}
],
"inputsInline": true,
"colour": "EXAMPLES"
},

Example #2 (result)
Checker arrays: ['statement_block_connector'] ['STATEMENT_CONNECTOR']
Blocks are connected!!!
container_block_implementation currentBlock: null  // <--- this is bad, though checker is working fine :)

frm...@gmail.com

unread,
Jul 27, 2022, 5:41:25 PM7/27/22
to Blockly
Hi Maribeth,

Could you please confirm that the code sample generator works on your end?
I am not able to make it work :(.
Cheers,

Oded

Maribeth Bottorff

unread,
Jul 27, 2022, 8:52:53 PM7/27/22
to Blockly
Unfortunately I'm still not able to reproduce your issue, so there might be a problem in some of your other code that might interact with this. If you can recreate a minimal instance of the bug and provide it as runnable code that's probably the next step for us to be able to help in-depth.

Some other troubleshooting I would try is to add a breakpoint in the code generator and see if you spot anything funky with the block at that time. You can inspect the Block object in the developer console and see what inputs and connections it has, etc and you may be able to notice if something is off there.

frm...@gmail.com

unread,
Jul 29, 2022, 3:44:43 PM7/29/22
to Blockly
Still fighting it, will update on progress.
Enjoy your weekend!

frm...@gmail.com

unread,
Aug 1, 2022, 5:33:50 PM8/1/22
to Blockly
Found the issue, was indeed not in the checker.
Debugged by removing the checker and trying to replicate.
The fact that you checked the code was not a waste of time, it really really helped. 

Reply all
Reply to author
Forward
0 new messages