Compare commits

..

No commits in common. "main" and "1.4" have entirely different histories.
main ... 1.4

39 changed files with 540 additions and 12208 deletions

50
advremote.lua Normal file
View File

@ -0,0 +1,50 @@
local ws = assert(http.websocket("ws://127.0.0.1:5133"))
print("connected to server")
local id
local isrunning = true
function exitcheck()
while true do
local event = os.pullEventRaw("terminate")
if event == "terminate" then
print("Exiting...")
isrunning = false
ws.close()
break
end
end
end
function main()
while isrunning do
print("ready")
local message, error = ws.receive()
if message then
print("Received message:", message)
if message == "ping" then
ws.send("pong")
elseif message == "sendcode" then
local file = io.open("main.lua", "w")
print("waiting for code")
local filedata, error = ws.receive()
file:write(filedata)
file:close()
elseif message == "runcode" then
id = multishell.launch({}, "main.lua")
multishell.setTitle(id, "Code")
multishell.setFocus(id)
elseif message == "exit" then
print("Exiting...")
break
end
else
print("WebSocket error:", error)
break
end
end
end
parallel.waitForAny(exitcheck, main)
print("Exited")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -218,7 +218,7 @@
"tooltip": "Send message formatted to player chat"
},
"advanced_peripherals_chatbox_send_toast_formatted_to_player": {
"message0": "Chatbox %1 toast send formatted json %2 and title %3 to player %4\nPrefix %5 Brackets %6 Bracket color %7 Range %8",
"message0": "Chatbox %1 toast send %2 and title %3 to player %4\nPrefix %5 Brackets %6 Bracket color %7 Range %8",
"args0": [
{
"type": "input_value",
@ -227,7 +227,7 @@
},
{
"type": "input_value",
"name": "JSON",
"name": "TEXT",
"check": "String"
},
{
@ -1508,301 +1508,5 @@
"nextStatement": null,
"colour": 200,
"tooltip": "Sets the redstone level output on the given side to the given power level. (0-15)"
},
"advanced_peripherals_mc_beacon_get_level": {
"message0": "Get level from beacon %1",
"args0": [
{
"type": "input_value",
"name": "BEACON",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the level of the Beacon."
},
"advanced_peripherals_mc_beacon_get_primary_effect": {
"message0": "Get primary effect from beacon %1",
"args0": [
{
"type": "input_value",
"name": "BEACON",
"check": "Peripheral"
}
],
"output": "String",
"colour": 200,
"tooltip": "Returns the registry name of the beacon's primary effect."
},
"advanced_peripherals_mc_beacon_get_secondary_effect": {
"message0": "Get secondary secondary effect from beacon %1",
"args0": [
{
"type": "input_value",
"name": "BEACON",
"check": "Peripheral"
}
],
"output": "String",
"colour": 200,
"tooltip": "Returns the registry name of the beacon's secondary effect."
},
"advanced_peripherals_mc_note_block_play_note": {
"message0": "Play current note sound at note block %1",
"args0": [
{
"type": "input_value",
"name": "NOTEBLOCK",
"check": "Peripheral"
}
],
"previousStatement": null,
"nextStatement": null,
"colour": 200,
"tooltip": "Plays the Note Block's current note sound."
},
"advanced_peripherals_mc_note_block_get_note": {
"message0": "Get current note from note block %1",
"args0": [
{
"type": "input_value",
"name": "NOTEBLOCK",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the index for the Note Block's current note. A number from 0 to 24."
},
"advanced_peripherals_mc_note_block_change_note_by": {
"message0": "Change note to %1 at note block %2",
"args0": [
{
"type": "input_value",
"name": "TO",
"check": "Number"
},
{
"type": "input_value",
"name": "NOTEBLOCK",
"check": "Peripheral"
}
],
"previousStatement": null,
"nextStatement": null,
"colour": 200,
"tooltip": "Changes the Note Block's note to the given note. note must be a number from 0 to 24."
},
"advanced_peripherals_mc_note_block_change_note": {
"message0": "Change note by 1 at note block %1",
"args0": [
{
"type": "input_value",
"name": "NOTEBLOCK",
"check": "Peripheral"
}
],
"previousStatement": null,
"nextStatement": null,
"colour": 200,
"tooltip": "Increments the Note Block's note to the next available note."
},
"advanced_peripherals_botania_flowers_get_mana": {
"message0": "Get mana from flower %1",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the amount of mana stored in the flower."
},
"advanced_peripherals_botania_flowers_get_max_mana": {
"message0": "Get max mana from flower %1",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the maximum amount of mana that the flower can hold."
},
"advanced_peripherals_botania_flowers_is_floating": {
"message0": "Is flower %1 floating",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the flower is a floating flower.",
"inputsInline": true
},
"advanced_peripherals_botania_flowers_is_on_enchanted_soil": {
"message0": "Is flower %1 on enchanted soil",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the flower is placed on enchanted soil.",
"inputsInline": true
},
"advanced_peripherals_botania_flowers_is_empty": {
"message0": "Is flower %1 empty",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the Flower is empty.",
"inputsInline": true
},
"advanced_peripherals_botania_flowers_is_full": {
"message0": "Is flower %1 full",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the Flower is full.",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_get_mana": {
"message0": "Get mana from pool %1",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the amount of mana stored in the pool."
},
"advanced_peripherals_botania_mana_pool_get_max_mana": {
"message0": "Get max mana from pool %1",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the maximum amount of mana that the pool can hold."
},
"advanced_peripherals_botania_mana_pool_get_mana_needed": {
"message0": "Get amount of mana is pool %1 needed to fill full",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the amount of mana needed to fill the pool. Equivalent to getMaxMana() - getMana().",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_is_empty": {
"message0": "Is pool %1 empty",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the Mana Pool is empty.",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_is_full": {
"message0": "Is pool %1 full",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the Mana Pool is full.",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_can_charge_item": {
"message0": "Can pool %1 change item",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if mode of the Mana Pool is set to charge the items on it.",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_has_items": {
"message0": "Is pool %1 has item",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the Mana Pool has at least one item on it.",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_get_items": {
"message0": "get items from pool %1 ",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Array",
"colour": 200,
"tooltip": "Returns a table with the items lying on the Mana Pool."
}
}

View File

@ -553,138 +553,3 @@ luaGenerator.forBlock['advanced_peripherals_redstone_integrator_set_analog_outpu
return `${integrator}.setOutput("${side}", ${power})\n`;
};
// Generator for Minecraft: Beacon
luaGenerator.forBlock['advanced_peripherals_mc_beacon_get_level'] = function(block, generator) {
var beacon = generator.valueToCode(block, 'BEACON', generator.ORDER_ATOMIC);
return [`${beacon}.getLevel()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_mc_beacon_get_primary_effect'] = function(block, generator) {
var beacon = generator.valueToCode(block, 'BEACON', generator.ORDER_ATOMIC);
return [`${beacon}.getPrimaryEffect()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_mc_beacon_get_secondary_effect'] = function(block, generator) {
var beacon = generator.valueToCode(block, 'BEACON', generator.ORDER_ATOMIC);
return [`${beacon}.getSecondaryEffect()`, luaGenerator.ORDER_NONE];
};
// Generator for Minecraft: Note Block
luaGenerator.forBlock['advanced_peripherals_mc_note_block_play_note'] = function(block, generator) {
var noteblock = generator.valueToCode(block, 'NOTEBLOCK', generator.ORDER_ATOMIC);
return `${noteblock}.playNote()\n`;
};
luaGenerator.forBlock['advanced_peripherals_mc_note_block_get_note'] = function(block, generator) {
var noteblock = generator.valueToCode(block, 'NOTEBLOCK', generator.ORDER_ATOMIC);
return [`${beacon}.getNote()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_mc_note_block_change_note_by'] = function(block, generator) {
var to = generator.valueToCode(block, 'TO', generator.ORDER_ATOMIC);
var noteblock = generator.valueToCode(block, 'NOTEBLOCK', generator.ORDER_ATOMIC);
return `${noteblock}.changeNoteBy(${to})\n`;
};
luaGenerator.forBlock['advanced_peripherals_mc_note_block_change_note'] = function(block, generator) {
var noteblock = generator.valueToCode(block, 'NOTEBLOCK', generator.ORDER_ATOMIC);
return `${noteblock}.changeNote()\n`;
};
// Generator for Botania: Flowers
luaGenerator.forBlock['advanced_peripherals_botania_flowers_get_mana'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.getMana()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_flowers_get_max_mana'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.getMaxMana()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_flowers_is_floating'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.isFloating()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_flowers_is_on_enchanted_soil'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.isOnEnchantedSoil()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_flowers_is_empty'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.isEmpty()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_flowers_is_full'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.isFull()`, luaGenerator.ORDER_NONE];
};
// Generator for Botania: Mana Pool
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_get_mana'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.getMana()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_get_max_mana'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.getMaxMana()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_get_mana_needed'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.getManaNeeded()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_is_empty'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.isEmpty()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_is_full'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.isFull()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_can_charge_item'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.canChargeItem()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_has_items'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.hasItems()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_get_items'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.getItems()`, luaGenerator.ORDER_NONE];
};

View File

@ -2,16 +2,14 @@
"name": "Advanced Peripherals",
"author": "DPSoftware Foundation",
"description": "Advanced Peripherals is a mod that adds many useful extensions for CC:Tweaked.",
"version": "1.2",
"version": "1.0.0",
"category": "Mod",
"keyword": "mod",
"license": "GPL-3.0-or-later",
"peripherals": true,
"library": false,
"require_network": false,
"dependencies": [
"CCPeripheral"
],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -45,7 +45,7 @@
<shadow type="logic_null"></shadow>
</value>
</block>
<block type="advanced_peripherals_chatbox_send_toast_to_player">
<block type="advanced_peripherals_chatbox_send_message_to_player">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">Hello, world!</field>
@ -107,30 +107,6 @@
<shadow type="logic_null"></shadow>
</value>
</block>
<block type="advanced_peripherals_chatbox_send_toast_formatted_to_player">
<value name="TITLE">
<shadow type="text">
<field name="TEXT">Hi</field>
</shadow>
</value>
<value name="PLAYER">
<shadow type="text">
<field name="TEXT">Player</field>
</shadow>
</value>
<value name="PREFIX">
<shadow type="logic_null"></shadow>
</value>
<value name="BRACKET">
<shadow type="logic_null"></shadow>
</value>
<value name="BRACKETCOLOR">
<shadow type="logic_null"></shadow>
</value>
<value name="RANGE">
<shadow type="logic_null"></shadow>
</value>
</block>
<label text="Energy Detector"></label>
<block type="advanced_peripherals_energy_detector_get_transfer_rate"></block>
@ -486,41 +462,5 @@
<label text="RS Bridge"></label>
<label text="Colony Integrator"></label>
<label text="Minecraft: Beacon"></label>
<block type="advanced_peripherals_mc_beacon_get_level"></block>
<block type="advanced_peripherals_mc_beacon_get_primary_effect"></block>
<block type="advanced_peripherals_mc_beacon_get_secondary_effect"></block>
<label text="Minecraft: Note Block"></label>
<block type="advanced_peripherals_mc_note_block_play_note"></block>
<block type="advanced_peripherals_mc_note_block_get_note"></block>
<block type="advanced_peripherals_mc_note_block_change_note_by">
<value name="TO">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="advanced_peripherals_mc_note_block_change_note"></block>
<label text="Botania: Flowers"></label>
<block type="advanced_peripherals_botania_flowers_get_mana"></block>
<block type="advanced_peripherals_botania_flowers_get_max_mana"></block>
<block type="advanced_peripherals_botania_flowers_is_floating"></block>
<block type="advanced_peripherals_botania_flowers_is_on_enchanted_soil"></block>
<block type="advanced_peripherals_botania_flowers_is_empty"></block>
<block type="advanced_peripherals_botania_flowers_is_full"></block>
<label text="Botania: Mana Pool"></label>
<block type="advanced_peripherals_botania_mana_pool_get_mana"></block>
<block type="advanced_peripherals_botania_mana_pool_get_max_mana"></block>
<block type="advanced_peripherals_botania_mana_pool_get_mana_needed"></block>
<block type="advanced_peripherals_botania_mana_pool_is_empty"></block>
<block type="advanced_peripherals_botania_mana_pool_is_full"></block>
<block type="advanced_peripherals_botania_mana_pool_can_charge_item"></block>
<block type="advanced_peripherals_botania_mana_pool_has_items"></block>
<block type="advanced_peripherals_botania_mana_pool_get_items"></block>
</category>
</xml>

View File

@ -9,7 +9,7 @@
"peripherals": false,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": false,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": true,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": false,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": true,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,9 +9,7 @@
"peripherals": true,
"library": false,
"require_network": false,
"dependencies": [
"CCPeripheral"
],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,9 +9,7 @@
"peripherals": true,
"library": false,
"require_network": false,
"dependencies": [
"CCPeripheral"
],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,9 +9,7 @@
"peripherals": true,
"library": false,
"require_network": false,
"dependencies": [
"CCPeripheral"
],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": true,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": false,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": false,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": false,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": false,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": false,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": false,
"adv": false,

View File

@ -9,9 +9,7 @@
"peripherals": true,
"library": false,
"require_network": false,
"dependencies": [
"CCPeripheral"
],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": false,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": true,
"adv": true,

View File

@ -9,7 +9,7 @@
"peripherals": false,
"library": false,
"require_network": false,
"dependencies": [],
"dependencies": {},
"design_for_computer": {
"basic": false,
"adv": false,

View File

@ -6,15 +6,14 @@ const path = require('path');
const pino = require('pino')
const pretty = require('pino-pretty');
const https = require('node:https');
const LocalStorage = require('node-localstorage').LocalStorage
const ipc = ipcMain
const logger = pino(pretty())
const localStorage = new LocalStorage('.');
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
var ena_splash = process.env['CCIDE_ENABLE_MAIN_SPLASH'] == 'true';
var currentprojectpath = null;
var currentprojectname = null;
var currentprojectopen = false;
@ -112,16 +111,12 @@ app.whenReady().then(async () => {
logger.error('Error in update check:', error);
}
logger.info("Current version: " + version)
logger.info("Version in github: " + gitver)
logger.info("Current version: " + version)
const normalizedAppVersion = normalizeVersion(version);
if (!gitver) {
logger.error("Can't check update")
} else {
const normalizedReleaseVersion = normalizeVersion(gitver);
if (normalizedAppVersion >= normalizedReleaseVersion) {
logger.info("Software is up-to-date.");
} else {
@ -159,7 +154,6 @@ app.whenReady().then(async () => {
}
}
}
}
logger.info("Initializing main windows...")
splash.webContents.send("change-status", "Initializing main windows...")
@ -174,7 +168,7 @@ app.whenReady().then(async () => {
enableRemoteModule: true,
contextIsolation: false,
},
show: ena_splash,
show: false,
center: true,
})
@ -224,12 +218,9 @@ app.whenReady().then(async () => {
});
ipc.on('update-log-status', (event, status) => {
logger.info(status);
logger.info(status)
if (!appstarted) {
splash.webContents.send("change-status", status);
}
if (status == "Downloading blocks...") {
win.show();
splash.webContents.send("change-status", status)
}
});

10639
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,20 @@
{
"name": "ccide",
"version": "1.6.0",
"version": "1.4",
"description": "ComputerCraft mod virtual lua IDE",
"main": "dist/main.js",
"main": "index.js",
"scripts": {
"dev": "title ccIDE && electron .",
"build": "title building ccIDE && electron-packager . ccIDE --platform=win32 --arch=x64 --icon=assets/ccIDEIcon.ico --out=dist --overwrite && for /d %i in (dist\\ccIDE-*) do copy package.json %i\\",
"build_debug": "title building ccIDE (Debug) && electron-packager . ccIDE --platform=win32 --arch=x64 --icon=assets/ccIDEIcon.ico --out=dist_debug --overwrite --debug && for /d %i in (dist_debug\\ccIDE-*) do copy package.json %i\\",
"new_build": "webpack --config webpack.main.config.js && webpack --config webpack.renderer.config.js",
"dist": "electron-builder"
"dev": "electron .",
"build": "electron-packager . ccIDE --platform=win32 --arch=x64 --icon=assets/ccIDEIcon.ico --out=dist --overwrite && for /d %i in (dist\\ccIDE-*) do copy package.json %i\\",
"build_debug": "electron-packager . ccIDE --platform=win32 --arch=x64 --icon=assets/ccIDEIcon.ico --out=dist_debug --overwrite --debug && for /d %i in (dist_debug\\ccIDE-*) do copy package.json %i\\"
},
"author": "DPSoftware Foundation",
"license": "GPL-3.0-or-later",
"devDependencies": {
"copy-webpack-plugin": "^12.0.2",
"electron": "^31.7.7",
"electron-builder": "^25.1.8",
"html-webpack-plugin": "^5.6.3",
"terser-webpack-plugin": "^5.3.11",
"webpack": "^5.97.1",
"webpack-cli": "^6.0.1",
"webpack-merge": "^6.0.1",
"webpack-node-externals": "^3.0.0"
"electron": "^31.1.0"
},
"dependencies": {
"@blockly/plugin-workspace-search": "^9.0.5",
"@blockly/theme-dark": "^7.0.7",
"@electron/remote": "^2.1.2",
"blockly": "^11.1.1",
"bootstrap": "^5.3.3",
@ -34,19 +23,6 @@
"open": "^10.1.0",
"pino": "^9.3.2",
"pino-pretty": "^11.2.2",
"toastify-js": "^1.12.0",
"unzipper": "^0.12.3",
"xmldom": "^0.6.0"
},
"build": {
"appId": "dev.dpsoftware.ccide",
"productName": "ccIDE",
"files": [
"dist/**/*",
"node_modules/**/*"
],
"directories": {
"output": "release"
}
}
}

View File

@ -6,18 +6,34 @@ special thank for [ccblockly](https://github.com/Mirka1405/ccblockly) for idea
![Screenshot 2024-08-20 202018](https://github.com/user-attachments/assets/ba6c1ca6-ce91-41c9-b412-b1f3a8f2e735)
# Quick Start
1. install nodejs and git.
2. git clone and cd to this project .
3. Install dependency use `npm install .`.
1. install nodejs and git
2. git clone cd to this project
3. Install dependency use `npm install .`
4. To run this IDE use `npm run dev` or if you using windows you can use `run.bat` to run it.
5. Done!
## Install Remote code into computercraft
it very simple! to install Remote code.
### Run from URL
for advanced computer/pocket/turtle
```
wget https://raw.githubusercontent.com/DPSoftware-Foundation/ccIDE/main/startup.lua
wget run https://raw.githubusercontent.com/DPSoftware-Foundation/ccIDE/main/advremote.lua
```
for non advance computer/pocket/turtle
```
wget run https://raw.githubusercontent.com/DPSoftware-Foundation/ccIDE/main/remote.lua
```
### Download and Run
for advanced computer/pocket/turtle
```
wget https://raw.githubusercontent.com/DPSoftware-Foundation/ccIDE/main/advremote.lua advremote.lua
advremote
```
for non advanced computer/pocket/turtle
```
wget https://raw.githubusercontent.com/DPSoftware-Foundation/ccIDE/main/remote.lua remote.lua
remote
```
And restart the computer.
If error "Domain not permitted" try [this solution](https://github.com/cc-tweaked/CC-Tweaked/discussions/626#discussioncomment-241924).
## official support library, peripheral and module function
@ -134,7 +150,7 @@ https://github.com/user-attachments/assets/195231d4-8fd8-4101-8068-70bc038a5c4f
https://github.com/user-attachments/assets/8f114cfa-d87c-47d0-a670-a13dc975ab06
# For adapting in other project
This project is for every block based IDE from DPSoftware Foundation.
This project is based for every block based IDE from DPSoftware Foundation
# License
This project is licensed under the [GPL v3 License](https://github.com/DPSoftware-Foundation/ccIDE/blob/main/LICENSE).

48
remote.lua Normal file
View File

@ -0,0 +1,48 @@
local ws = assert(http.websocket("ws://127.0.0.1:5133"))
print("connected to server")
local id
local isrunning = true
function exitcheck()
while true do
local event = os.pullEventRaw("terminate")
if event == "terminate" then
print("Exiting...")
isrunning = false
ws.close()
break
end
end
end
function main()
while isrunning do
print("ready")
local message, error = ws.receive()
if message then
print("Received message:", message)
if message == "ping" then
ws.send("pong")
elseif message == "sendcode" then
local file = io.open("main.lua", "w")
print("waiting for code")
local filedata, error = ws.receive()
file:write(filedata)
file:close()
elseif message == "runcode" then
shell.run("main")
elseif message == "exit" then
print("Exiting...")
break
end
else
print("WebSocket error:", error)
break
end
end
end
parallel.waitForAny(exitcheck, main)
print("Exited")

View File

@ -1,5 +1,3 @@
@echo off
title Starting ccIDE
start "" cmd /c "npm cache clean --force"
timeout 2
npm run dev

View File

@ -1,110 +1,11 @@
const fs = require('fs');
const path = require('path');
const https = require('https');
const unzipper = require('unzipper');
const { DOMParser, XMLSerializer } = require('xmldom');
const peripheralsfolder = path.join(__dirname, "../blocks");
const fallbackImagePath = path.join(__dirname, '..', 'assets', 'noimagefallback.png'); // Path to fallback image
if (!fs.existsSync(peripheralsfolder)) {
// If it doesn't exist, create it
fs.mkdirSync(peripheralsfolder, { recursive: true });
console.log('Block Folder created');
}
let progressBar = document.getElementById('progressBarloading');
let blocks_url = "https://cdn.damp11113.xyz/file/zip/ccide/blockslastest.zip?dl=1"
function isBlocksFolderEmpty() {
try {
const files = fs.readdirSync(peripheralsfolder);
return files.length === 0; // Returns true if the folder is empty
} catch (err) {
console.error('Error reading folder:', err);
return false; // Return false or handle error as needed
}
}
async function downloadBlocks() {
try {
progressBar.style.display = 'inline-block';
// Create the output directory if it doesn't exist
if (!fs.existsSync(peripheralsfolder)) {
fs.mkdirSync(peripheralsfolder, { recursive: true });
}
console.log('Downloading blocks...');
const zipFilePath = path.join(peripheralsfolder, 'blocks.zip');
// Download the file as a Promise
await new Promise((resolve, reject) => {
https.get(blocks_url, (response) => {
if (response.statusCode !== 200) {
reject(new Error(`Download failed with status code ${response.statusCode}`));
return;
}
const totalBytes = parseInt(response.headers['content-length'], 10);
let downloadedBytes = 0;
const file = fs.createWriteStream(zipFilePath);
response.on('data', (chunk) => {
downloadedBytes += chunk.length;
const percentCompleted = Math.round((downloadedBytes / totalBytes) * 100);
progressBar.value = percentCompleted;
progressBar.innerText = percentCompleted + '%'; // Fallback text
process.stdout.write(`Download progress: ${percentCompleted}%\r`);
});
response.pipe(file);
file.on('finish', () => {
file.close();
console.log('\nFile downloaded successfully.');
resolve();
});
file.on('error', (error) => {
fs.unlinkSync(zipFilePath);
reject(error);
});
}).on('error', (error) => {
reject(error);
});
});
console.log('Unzipping file...');
// Unzip the file as a Promise
await new Promise((resolve, reject) => {
fs.createReadStream(zipFilePath)
.pipe(unzipper.Extract({ path: peripheralsfolder }))
.on('close', () => {
console.log('File unzipped successfully.');
fs.unlinkSync(zipFilePath); // Delete the zip file after extraction
console.log('Zip file deleted.');
console.log('Downloaded blocks');
progressBar.style.display = 'none';
resolve();
})
.on('error', (error) => {
reject(error);
});
});
} catch (error) {
console.error('Error downloading or unzipping file:', error);
}
}
const defineicon = {
computer: {
@ -140,26 +41,23 @@ function mergeXml(xml1, xml2) {
return mergedXml;
}
function loadperipheral(workspace, currenttoolbar, peripherals, usedlibinproject) {
if (!usedlibinproject.includes(peripherals)) {
try {
console.log(`Importing ${peripherals} blocks`);
function loadperipheral(workspace, currenttoolbar, peripherals) {
console.log(`Importing ${peripherals} blocks`)
const filePath = path.join(peripheralsfolder, peripherals);
const jsonfilePath = path.join(filePath, "block_design.json");
const xmlfilePath = path.join(filePath, "toolbox.xml");
const generatorfilePath = path.join(filePath, "generator.js");
const indexfilePath = path.join(filePath, "index.json");
const generatorfilePath = path.join(filePath, "generator.js"); // Path to generator.js
// Synchronously read and parse the index.json file
const indexcontent = JSON.parse(fs.readFileSync(indexfilePath, 'utf8'));
const dependencies = indexcontent.dependencies;
dependencies.forEach((dependency) => {
currenttoolbar = loadperipheral(workspace, currenttoolbar, dependency, usedlibinproject);
});
// Synchronously load and parse block_design.json
const blocksJson = JSON.parse(fs.readFileSync(jsonfilePath, 'utf8'));
// Load generator.js
// Load blocks from block_design.json
fs.readFile(jsonfilePath, 'utf-8', (err, data) => {
if (err) {
console.error('Error loading JSON file:', err);
return;
}
try {
const blocksJson = JSON.parse(data);
for (const blockId in blocksJson) {
if (blocksJson.hasOwnProperty(blockId)) {
Blockly.Blocks[blockId] = {
@ -169,28 +67,25 @@ function loadperipheral(workspace, currenttoolbar, peripherals, usedlibinproject
};
}
}
} catch (e) {
document.getElementById('statusMessage').textContent = 'Error parsing JSON file: ' + e;
return;
}
});
// Synchronously load and merge new toolbox XML
// Load and merge new toolbox XML
const toolbar = fs.readFileSync(xmlfilePath, 'utf8');
const newxml = mergeXml(currenttoolbar, toolbar);
workspace.updateToolbox(newxml);
fireNotify(`Loaded ${peripherals}`, "success")
document.getElementById('statusMessage').textContent = `Loaded ${peripherals}`;
// Synchronously require generator.js
require(generatorfilePath); // This will execute generator.js if it's a Node.js module
console.log(`Loaded ${peripherals} blocks`);
usedlibinproject.push(peripherals);
console.log(`Loaded ${peripherals} blocks`)
return newxml;
} catch (e) {
fireNotify(`Can't Import ${peripherals}: ${e}`, "error")
ipc.send("error", `Can't Import ${peripherals}: ${e}`);
}
} else {
return currenttoolbar;
}
}
function extractFolderName(path) {
@ -224,6 +119,7 @@ function addimageiconinfo(div, src, tiptool) {
function scanindex() {
let foundedpackages = 0;
document.getElementById('statusMessage').textContent = "Scanning Packages...";
// clear all item in libcontainer
document.getElementById('libcontainer').innerHTML = "";
@ -271,15 +167,9 @@ function scanindex() {
// Create the title element
const title = document.createElement('h3');
title.textContent = jsonData.name;
title.textContent = jsonData.name + ` [v${jsonData.version} by ${jsonData.author}]`;
libraryDetails.appendChild(title);
// Create the credit and version element
const cv = document.createElement('p');
cv.classList.add('library-details-cv');
cv.textContent = `v${jsonData.version} by ${jsonData.author}`;
libraryDetails.appendChild(cv);
// Create the description element
const description = document.createElement('p');
description.innerHTML = jsonData.description;
@ -334,12 +224,14 @@ function scanindex() {
});
});
fireNotify(`Founded ${foundedpackages} Packages`, "success")
document.getElementById('statusMessage').textContent = `Founded ${foundedpackages} Packages`;
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
}
module.exports = {
loadperipheral,
scanindex,
isBlocksFolderEmpty,
downloadBlocks
scanindex
}

View File

@ -7,258 +7,109 @@ class CCRemote {
host: ip
});
this.clients = new Map(); // Map to track client data (numeric IDs)
this.clientIdCounter = 0; // Counter for numeric client IDs
console.log("Remote server is started");
fireNotify(
"Remote server started, waiting for clients to connect...",
"info"
);
this.socket.on('connection', (ws) => {
const clientId = this.clientIdCounter++;
this.clients.set(clientId, { ws, isAlive: true, name: `Client-${clientId}`, clientInfo: null });
if (this.clients.size >= 0) {
document.getElementById("navbar-button-computer-disconnect").disabled = false;
document.getElementById("navbar-button-computer-run").disabled = false;
}
console.log(`Client ${clientId} connected.`);
fireNotify(`Client ${clientId} connected`, "success");
document.getElementById('statusMessage').textContent = "Computer connected";
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
// Send "info" command to the client immediately upon connection
this.sendCommandToClient(clientId, 'info', true);
console.log('WebSocket connection established.');
// Set up heartbeat
ws.isAlive = true;
ws.on('pong', () => {
this.clients.get(clientId).isAlive = true;
ws.isAlive = true;
});
ws.on('message', (message) => {
console.log(`Message from Client ${clientId}: ${message}`);
// Handle info response
try {
const data = JSON.parse(message);
if (data.OSVersion && typeof data.OSVersion === 'string' &&
data.Name && typeof data.Name === 'string' &&
typeof data.ID === 'number' &&
typeof data.uptime === 'number' &&
typeof data.Type === 'number') {
// Update clientInfo with the received data
this.clients.get(clientId).clientInfo = data;
console.log(`Client ${clientId} info updated:`, data);
} else {
console.log(`Invalid data format from Client ${clientId}:`, data);
}
} catch (error) {
console.error(`Error parsing message from Client ${clientId}:`, error);
}
console.log(`Received message => ${message}`);
});
ws.on('close', () => {
console.log(`Client ${clientId} disconnected.`);
this.clients.delete(clientId);
fireNotify(`Client ${clientId} disconnected`, "warning");
if (this.clients.size === 0) {
document.getElementById("navbar-button-computer-disconnect").disabled = true;
document.getElementById("navbar-button-computer-run").disabled = true;
}
document.getElementById('statusMessage').textContent = "Computer disconnected";
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
});
ws.on('error', (error) => {
console.error(`Client ${clientId} error:`, error);
fireNotify(`Error with Client ${clientId}: ${error.message}`, "error");
console.error('Client error:', error);
});
});
// Ping clients every 30 seconds to check if they are alive
const interval = setInterval(() => {
for (const [clientId, clientData] of this.clients.entries()) {
if (clientData.isAlive === false) {
console.log(`Client ${clientId} did not respond to ping, terminating connection.`);
fireNotify(`Client ${clientId} did not respond to ping, disconnected`, "warning");
clientData.ws.terminate();
this.clients.delete(clientId);
continue;
this.socket.clients.forEach((ws) => {
if (ws.isAlive === false) {
console.log('Client did not respond to ping, terminating connection.');
return ws.terminate();
}
clientData.isAlive = false;
clientData.ws.ping();
}
}, 30000);
ws.isAlive = false;
ws.ping();
});
}, 1000);
this.socket.on('close', () => {
clearInterval(interval);
console.log('WebSocket server closed.');
fireNotify("WebSocket server closed.", "info");
});
this.socket.on('error', (error) => {
console.error('WebSocket server error:', error);
fireNotify(`WebSocket server error: ${error.message}`, "error");
});
}
// List all connected clients with their IDs, names, and clientInfo
// List all connected clients with their IDs, names, and clientInfo
async listClients() {
const clientList = [];
// Create an array of promises for fetching client info
const infoPromises = [];
// Loop over each client and trigger 'info' command to get the latest information
for (const [clientId, clientData] of this.clients.entries()) {
if (clientData.ws.readyState === WebSocket.OPEN) {
const infoPromise = new Promise((resolve) => {
// Set a timeout to handle cases where no response is received
const timeout = setTimeout(() => {
resolve({ id: clientId, name: clientData.name, info: null });
}, 5000); // Wait 5 seconds for a response
// Listen for the response from the client
const messageHandler = (message) => {
try {
const data = JSON.parse(message);
if (data.OSVersion && typeof data.OSVersion === 'string' &&
data.Name && typeof data.Name === 'string' &&
typeof data.ID === 'number' &&
typeof data.uptime === 'number' &&
typeof data.Type === 'number') {
// Resolve with the valid data
resolve({ id: clientId, name: clientData.name, info: data });
} else {
resolve({ id: clientId, name: clientData.name, info: null });
}
} catch (error) {
resolve({ id: clientId, name: clientData.name, info: null });
}
};
// Send the "info" command to the client
this.sendCommandToClient(clientId, 'info', true);
// Attach the handler for this specific client
clientData.ws.on('message', messageHandler);
});
// Add the promise to the array
infoPromises.push(infoPromise);
}
}
// Wait for all promises to resolve (all client info to be updated)
const resolvedClientInfo = await Promise.all(infoPromises);
// Construct the final list of clients with updated information
for (const clientInfo of resolvedClientInfo) {
clientList.push(clientInfo);
}
return clientList;
}
// Check if any client is connected
isClientConnect() {
return this.clients.size > 0;
for (const client of this.socket.clients) {
if (client.readyState === WebSocket.OPEN) {
return true;
}
}
return false;
}
// Send code to all clients
sendCode(code) {
this.clients.forEach((clientData, clientId) => {
if (clientData.ws.readyState === WebSocket.OPEN) {
clientData.ws.send("sendcode");
sendCommand(command) {
this.socket.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(command);
}
});
}
sendCode(Code) {
this.socket.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send("sendcode");
setTimeout(() => {
clientData.ws.send(code);
client.send(Code);
}, 500);
}
});
}
// Send code to a specific client
sendCodeToClient(clientId, code) {
const client = this.clients.get(clientId);
if (!client || client.ws.readyState !== WebSocket.OPEN) {
console.error(`Client ${clientId} is not available or not connected.`);
fireNotify(`Client ${clientId} is not connected.`, "error");
return;
}
client.ws.send("sendcode");
setTimeout(() => {
client.ws.send(code);
}, 500);
}
// Run code on all clients
runCode() {
this.clients.forEach((clientData, clientId) => {
if (clientData.ws.readyState === WebSocket.OPEN) {
clientData.ws.send("runcode");
this.socket.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send("runcode");
}
});
}
// Run code on a specific client
runCodeOnClient(clientId) {
const client = this.clients.get(clientId);
if (!client || client.ws.readyState !== WebSocket.OPEN) {
console.error(`Client ${clientId} is not available or not connected.`);
fireNotify(`Client ${clientId} is not connected.`, "error");
return;
}
client.ws.send("runcode");
}
// Disconnect all clients
disconnectAllClients() {
this.clients.forEach((clientData, clientId) => {
if (clientData.ws.readyState === WebSocket.OPEN) {
clientData.ws.send("exit");
clientData.ws.close();
fireNotify(`Client ${clientId} disconnected`, "warning");
this.socket.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.close();
}
});
this.clients.clear();
}
// Send a command to a specific client
sendCommandToClient(clientId, command, expectResponse = false) {
const client = this.clients.get(clientId);
if (!client || client.ws.readyState !== WebSocket.OPEN) {
console.error(`Client ${clientId} is not available or not connected.`);
fireNotify(`Client ${clientId} is not connected.`, "error");
return;
}
client.ws.send(command);
if (expectResponse) {
client.ws.on('message', (response) => {
console.log(`Response from Client ${clientId}: ${response}`);
});
}
}
// Send a command to all clients
sendCommandToAll(command, expectResponse = false) {
for (const [clientId, clientData] of this.clients.entries()) {
if (clientData.ws.readyState === WebSocket.OPEN) {
clientData.ws.send(command);
if (expectResponse) {
clientData.ws.on('message', (response) => {
console.log(`Response from Client ${clientId}: ${response}`);
});
}
}
}
}
}

View File

@ -9,16 +9,6 @@ const circles = document.querySelectorAll(".circle");
let upcurrentActive = 1;
let uploadError = false; // Flag to track if there's an error
const defineicon = {
5: path.join(__dirname, '..', 'assets', 'basic_computer.png'),
6: path.join(__dirname, '..', 'assets', 'adv_computer.png'),
7: path.join(__dirname, '..', 'assets', 'command_computer.png'),
2: path.join(__dirname, '..', 'assets', 'pocket_computer.png'),
4: path.join(__dirname, '..', 'assets', 'adv_pocket_computer.png'),
1: path.join(__dirname, '..', 'assets', 'turtle.png'),
3: path.join(__dirname, '..', 'assets', 'adv_turtle.png')
}
const uploadUpdateProgress = () => {
circles.forEach((circle, index) => {
if (index < upcurrentActive) {
@ -44,6 +34,7 @@ const uploadUpdateProgress = () => {
};
function clientexit() {
ccInstance.sendCommand("exit")
ccInstance.disconnectAllClients();
}
@ -51,17 +42,15 @@ function gencodeonly() {
return luaGenerator.workspaceToCode(workspace);
}
let selectedClientId = null;
async function gencode() {
console.log("Starting generate code")
document.getElementById('upload-popup').style.display = 'block';
upcurrentActive = 1;
uploadError = false;
uploadUpdateProgress();
// compile/convert code
// Compile/convert code
console.log("Generating code...");
console.log("Generating code...")
upcurrentActive++;
uploadUpdateProgress();
document.getElementById('upload-status').textContent = "Generating code";
@ -72,108 +61,37 @@ async function gencode() {
uploadError = true;
uploadUpdateProgress();
document.getElementById('upload-status').textContent = e;
return;
return
}
upcurrentActive++;
uploadUpdateProgress();
if (ccInstance.isClientConnect()) {
const clients = await ccInstance.listClients();
document.getElementById('upload-status').innerHTML = "";
const ClientLists = document.createElement('div');
ClientLists.classList.add("library-content");
clients.forEach(client => {
console.log(`Info:`, client.info);
const ClientItem = document.createElement('div');
ClientItem.classList.add('library-item', 'overflow-auto', 'library-container');
ClientItem.setAttribute('data-clientid', client.id);
const img = document.createElement('img');
img.classList.add('libimage');
img.src = defineicon[client.info.Type];
ClientItem.appendChild(img);
const ClientDetails = document.createElement('div');
ClientDetails.classList.add('library-details');
const title = document.createElement('h3');
title.textContent = client.info.Name;
ClientDetails.appendChild(title);
const description = document.createElement('p');
description.innerHTML = `OS: ${client.info.OSVersion} | ID: ${client.info.ID} | Uptime: ${client.info.uptime}s`;
ClientDetails.appendChild(description);
ClientItem.appendChild(ClientDetails);
// Add event listener for user selection
ClientItem.addEventListener('click', () => {
selectedClientId = client.id; // Correctly store the client ID
ClientItem.classList.add('selected'); // Optional: Style selected item
});
ClientLists.appendChild(ClientItem);
});
const title = document.createElement("h3");
title.textContent = "Upload to machine";
document.getElementById('upload-status').appendChild(title);
document.getElementById('upload-status').appendChild(ClientLists);
// Wait for user selection
const waitForSelection = () => new Promise((resolve, reject) => {
const checkSelection = setInterval(() => {
if (selectedClientId !== null) {
clearInterval(checkSelection);
resolve(selectedClientId);
}
}, 100); // Check every 100ms if a client is selected
});
// Wait until the user selects a client
try {
const selectedClientId = await waitForSelection();
console.log(`Selected client: ${selectedClientId}`);
document.getElementById('upload-status').innerHTML = "";
// Now proceed with uploading code to the selected client
console.log("Uploading code to machine...");
// upload to computercraft with remote
console.log("Uploading code to machine...")
document.getElementById('upload-status').textContent = "Uploading code to machine";
ccInstance.sendCodeToClient(selectedClientId, code);
await delay(500);
ccInstance.sendCode(code);
await delay(500)
// Execute with remote
console.log("Executing code in machine...");
// execute with remote
console.log("Executing code in machine...")
document.getElementById('upload-status').textContent = "Executing code";
upcurrentActive++;
uploadUpdateProgress();
ccInstance.runCodeOnClient(selectedClientId);
} catch (error) {
console.error("Error waiting for selection:", error);
uploadError = true;
uploadUpdateProgress();
document.getElementById('upload-status').innerHTML = `Error selecting client.`;
}
ccInstance.runCode();
} else {
console.log("Machine is not connected");
console.log("Machine is not connected")
uploadError = true;
uploadUpdateProgress();
document.getElementById('upload-status').innerHTML = `Please Connect Computer to IDE.\nInstruction: <a href="https://github.com/DPSoftware-Foundation/ccIDE#install-remote-code-into-computercraft">Install Remote code into computercraft</a> in github.`;
return;
document.getElementById('upload-status').innerHTML = `Please Connect Computer to IDE.\nInstruction: <a href="https://github.com/DPSoftware-Foundation/ccIDE#install-remote-code-into-computercraft">Install Remote code into computercraft</a> in github. (Please press SHIFT or CTRL and click)`;
return
}
// Done!
console.log("Run code done!");
// done!
console.log("Run code done!")
document.getElementById('upload-status').textContent = "Done!";
await delay(1000);
await delay(1000)
document.getElementById('upload-popup').style.animation = 'fadeOut 0.3s ease'; // Apply fade-out animation
selectedClientId = null;
setTimeout(function() {
document.getElementById('upload-popup').style.display = 'none'; // Hide popup after animation completes
document.getElementById('upload-popup').style.animation = ''; // Reset animation property

View File

@ -3,29 +3,15 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- <meta charset="utf-8" http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-m0mwdxEgv9nMDDw3gHrhX/xCs/OAiZg5/bQ/6X5SIcA='"/> -->
<div class="loading-area">
<div id="loadingContent">
<div class="loader"></div>
<br>
<h3>Please wait...</h3>
<h5 id="loadingstatus"></h5>
<progress id="progressBarloading" value="0" max="100"></progress>
<div class="dpclogoload">
<img src="../assets/DPSoftware2.png" style="width: 25%;"/>
</div>
</div>
</div>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;700&display=swap" rel="stylesheet">
<script>
const {shell} = require('electron');
</script>
<link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="../node_modules/toastify-js/src/toastify.css">
<script src="../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>
<div class="content-area">
<!-- Navigation bar -->
<nav id="navbar">
<!--
@ -54,8 +40,13 @@
<div id="blocklyDiv"></div>
</section>
<div class="popup" id="upload-popup" style="overflow-y:hidden;">
<div class="popup-content p-3" style="max-width: 720px; position: relative; margin-top: 100px;">
<!-- Status bar -->
<div id="statusBar">
<p id="statusMessage">Initializing...</p>
</div>
<div class="popup" id="upload-popup">
<div class="popup-content">
<button type="button" class="btn-close float-end" aria-label="Close" id="uploadCloseBtn"></button>
<div class="upload-progress-container" style="text-align: center;">
<div class="progress-container">
@ -73,7 +64,7 @@
<svg viewBox="0 0 24 24"><path d="m20 22-3.86-1.55c.7-1.53 1.2-3.11 1.51-4.72L20 22M7.86 20.45 4 22l2.35-6.27c.31 1.61.81 3.19 1.51 4.72M12 2s5 2 5 10c0 3.1-.75 5.75-1.67 7.83A2 2 0 0 1 13.5 21h-3a2 2 0 0 1-1.83-1.17C7.76 17.75 7 15.1 7 12c0-8 5-10 5-10m0 10c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z" /></svg>
</div>
</div>
<div id="upload-status"></div>
<p id="upload-status"></p>
</div>
</div>
</div>
@ -118,9 +109,7 @@
<div class="library-item overflow-auto library-container" data-libraryfolder="name">
<img src="image.jpg" class="libimage" onerror="this.onerror=null;this.src='../assets/noimagefallback.png'; this.alt='No Image Available';">
<div class="library-details">
<h3>Title</h3>
<!--Add Author credit-->
<h6>v1.0 by Author</h6>
<h3>Title [v1.0 by Author]</h3>
<p>Library description goes here.</p>
<img src="../assets/basic_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Basic Computer">
<img src="../assets/adv_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Advanced Computer">
@ -134,21 +123,17 @@
<img src="../assets/network-require.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Network Require">
</div>
</div>
</div>
</div>
<div class="button-container">
<button type="button" class="btn btn-success btn-sm" id="packageman-import-btn">Import Packages</button>
<button type="button" class="btn btn-warning btn-sm" onclick='scanindex()'>Refetch Packages</button>
<button type="button" class="btn btn-primary btn-sm" onclick='downloadBlocks()'>Download Packages</button>
</div>
<button type="button" class="btn btn-primary btn-sm" onclick='shell.openExternal("https://damp11113.xyz/dpsoftware/ccide/library")'>Download Packages</button>
</div>
</div>
</div>
<script>
const {shell} = require('electron');
</script>
<script src="../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="frontend.js"></script>
<script src="index.js"></script>
<script src="codegen.js"></script>

View File

@ -20,43 +20,14 @@ console.originalLog = console.log;
console.log = function (...args) {
ipc.send("update-log-status", ...args)
console.originalLog(...args)
document.getElementById("loadingstatus").textContent = args.join(' ');
};
console.log("Importing module...")
const fs = require('fs');
const path = require('path');
const { loadperipheral, scanindex, isBlocksFolderEmpty, downloadBlocks } = require("./blocksmanager");
const { loadperipheral, scanindex } = require("./blocksmanager");
const Blockly = require('blockly');
const { WorkspaceSearch } = require("@blockly/plugin-workspace-search")
const Toastify = require('toastify-js');
function fireNotify(text, status, destination, duration=3000) {
let color;
if (status == "success") {
color = "#a5dc86"
} else if (status == "warning") {
color = "#f8bb86"
} else if (status == "error") {
color = "#f27474"
} else if (status == "info") {
color = "#3fc3ee"
}
Toastify({
text: text,
duration: duration,
destination: destination,
newWindow: true,
close: true,
gravity: "top", // `top` or `bottom`
position: "right", // `left`, `center` or `right`
stopOnFocus: true, // Prevents dismissing of toast on hover
style: {
background: color,
}
}).showToast();
}
let isprojectsaved = false;
let isprojectopened = false;
@ -68,24 +39,6 @@ Blockly.utils.colour.setHsvSaturation(0.9)
let originaltoolbar = fs.readFileSync(path.join(__dirname, "toolbox.xml"), 'utf8');
try {
const theme = Blockly.Theme.defineTheme('themeName', {
base: Blockly.Themes.Classic,
componentStyles: {
workspaceBackgroundColour: "#1e1e1e",
toolboxBackgroundColour: "blackBackground",
toolboxForegroundColour: "#fff",
flyoutBackgroundColour: "#252526",
flyoutForegroundColour: "#ccc",
flyoutOpacity: 1,
scrollbarColour: "#797979",
insertionMarkerColour: "#fff",
insertionMarkerOpacity: .3,
scrollbarOpacity: .4,
cursorColour: "#d0d0d0",
blackBackground: "#333"
}
});
var workspace = Blockly.inject('blocklyDiv', {
toolbox: originaltoolbar,
trashcan: true,
@ -103,8 +56,7 @@ try {
minScale: 0.1,
scaleSpeed: 1.1,
pinch: true
},
theme: theme
}
});
const workspaceSearch = new WorkspaceSearch(workspace);
@ -115,6 +67,27 @@ try {
ipc.send("erroronstart", `Error on initializing workspace: ${e}`)
}
try {
console.log("Importing system library...")
const sysmodulejson = fs.readFileSync(path.join(__dirname, "module_block_design.json"), 'utf8');
const blocksJson = JSON.parse(sysmodulejson);
for (const blockId in blocksJson) {
if (blocksJson.hasOwnProperty(blockId)) {
Blockly.Blocks[blockId] = {
init: function() {
this.jsonInit(blocksJson[blockId]);
}
};
}
}
require("./module_generator")
console.log("Scanning library...")
scanindex();
} catch (e) {
ipc.send("erroronstart", `Error on loading block: ${e}`)
}
console.log("Initializing event...")
ipc.on('export-lua-request', (event) => {
@ -125,7 +98,7 @@ ipc.on('export-lua-request', (event) => {
// Save workspace
ipc.on('save-workspace-request', (event) => {
console.log("Saving project...")
fireNotify(`Saving...`, "warning")
document.getElementById('statusMessage').textContent = `Saving...`;
const state = Blockly.serialization.workspaces.save(workspace);
const data = {
"usedlibrary": usedlibinproject,
@ -147,21 +120,28 @@ ipc.on('load-workspace', (event, json) => {
const packagefolder = libinproject[i]
if (!usedlibinproject.includes(packagefolder)) {
try {
originaltoolbar = loadperipheral(workspace, originaltoolbar, packagefolder, usedlibinproject);
originaltoolbar = loadperipheral(workspace, originaltoolbar, packagefolder);
usedlibinproject.push(packagefolder);
} catch (e) {
fireNotify(`Can't Import ${usedlibinproject[i]}: ${e}`, "error")
}
document.getElementById('statusMessage').textContent = `Can't Import ${usedlibinproject[i]}: ${e}`;
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
}
}
}
setTimeout(() => {
Blockly.serialization.workspaces.load(data.content, workspace);
isprojectsaved = true
fireNotify(`Project Loaded`, "success")
document.getElementById('statusMessage').textContent = `Project Loaded`;
}, 100);
}
} catch (e) {
fireNotify(`Can't Load Project: ${e}`, "error")
document.getElementById('statusMessage').textContent = `Can't Load Project: ${e}`;
}
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
})
ipc.on('workspace-saved', (event, success) => {
@ -169,7 +149,10 @@ ipc.on('workspace-saved', (event, success) => {
if (!isprojectopened) {
isprojectopened = true;
}
fireNotify(`Project Saved`, "success")
document.getElementById('statusMessage').textContent = `Project Saved`;
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
});
ipc.on("open-about", () => {
@ -202,54 +185,27 @@ document.getElementById("packageman-import-btn").addEventListener('click', () =>
const selectedItems = document.querySelectorAll('.library-item.selected');
selectedItems.forEach(item => {
const packagefolder = item.getAttribute('data-libraryfolder');
originaltoolbar = loadperipheral(workspace, originaltoolbar, packagefolder, usedlibinproject);
fireNotify(`Loaded ${packagefolder}`, "success")
if (!usedlibinproject.includes(packagefolder)) {
try {
originaltoolbar = loadperipheral(workspace, originaltoolbar, packagefolder);
usedlibinproject.push(packagefolder);
} catch (e) {
document.getElementById('statusMessage').textContent = `Can't Import ${packagefolder}: ${e}`;
}
}
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
});
});
async function InitializingBlock() {
try {
console.log("Importing system library...");
const sysmodulejson = fs.readFileSync(path.join(__dirname, "module_block_design.json"), 'utf8');
const blocksJson = JSON.parse(sysmodulejson);
for (const blockId in blocksJson) {
if (blocksJson.hasOwnProperty(blockId)) {
Blockly.Blocks[blockId] = {
init: function() {
this.jsonInit(blocksJson[blockId]);
}
};
}
}
require("./module_generator");
console.log("Check library folder...");
if (isBlocksFolderEmpty()) {
await downloadBlocks();
}
scanindex();
} catch (e) {
ipc.send("erroronstart", `Error on loading block: ${e}`);
}
// Ensure Blockly container is shown after the workspace is injected
console.log("Finished")
setTimeout(() => {
// Ensure Blockly container is shown after the workspace is injected
console.log("Finished")
setTimeout(() => {
ipc.send("ready")
// Hide loading area
const loadingArea = document.querySelector('.loading-area');
loadingArea.style.opacity = '0';
loadingArea.style.visibility = 'hidden';
loadingArea.style.display = 'none';
}, 500);
// Show content area
const contentArea = document.querySelector('.content-area');
contentArea.style.opacity = '1';
contentArea.style.visibility = 'visible';
}, 500);
}
InitializingBlock();
document.getElementById('statusMessage').textContent = "Computer isn't connect";
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 10000);

View File

@ -1,90 +1,16 @@
:root {
--line-border-fill: #3498db;
--line-border-empty: #7f8c8d; /* Lighter gray for dark mode */
--background-color: #121212; /* Dark background for body */
--text-color: #e0e0e0; /* Lighter text color */
--button-bg: #333; /* Dark button background */
--button-hover-bg: #0066cc; /* Button hover color */
--progress-bg: #3498db; /* Progress bar fill color */
--error-color: #e74c3c; /* Error state color */
--line-border-empty: #e0e0e0;
}
body {
background-color: var(--background-color);
color: var(--text-color);
font-family: 'Arial', sans-serif;
overflow: hidden; /* Hide scrollbars */
}
.content-area {
opacity: 0;
visibility: hidden;
}
.loading-area {
color: #cacaca;
position: absolute;
z-index: 400;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: calc(100% - 22px);
}
#progressBarloading {
display: none; /* Initially hidden */
}
#loadingContent {
position: relative;
background-color: #212121;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.loader {
width: 90px;
height: 14px;
box-shadow: 0 3px 0 #fff;
position: relative;
clip-path: inset(-40px 0 -5px)
}
.loader:before {
content: "";
position: absolute;
inset: auto calc(50% - 17px) 0;
height: 50px;
--g:no-repeat linear-gradient(#ccc 0 0);
background: var(--g),var(--g),var(--g),var(--g);
background-size: 16px 14px;
animation:
l7-1 2s infinite linear,
l7-2 2s infinite linear;
}
@keyframes l7-1 {
0%,
100% {background-position: 0 -50px,100% -50px}
17.5% {background-position: 0 100%,100% -50px,0 -50px,100% -50px}
35% {background-position: 0 100%,100% 100% ,0 -50px,100% -50px}
52.5% {background-position: 0 100%,100% 100% ,0 calc(100% - 16px),100% -50px}
70%,
98% {background-position: 0 100%,100% 100% ,0 calc(100% - 16px),100% calc(100% - 16px)}
}
@keyframes l7-2 {
0%,70% {transform:translate(0)}
100% {transform:translate(200%)}
}
.dpclogoload {
display: flex; /* Enable flexbox */
justify-content: center; /* Center horizontally */
position: fixed; /* Fixed position */
bottom: 30px; /* Adjust as necessary */
z-index: 20; /* Ensure it appears above other content */
width: 100%; /* Ensure the div takes full width */
height: 100vh;
font-family: 'Noto Sans', sans-serif;
overflow: hidden; /* Hide scrollbars */
}
#navbar {
@ -96,7 +22,7 @@ body {
}
.navbar-button {
background-color: var(--button-bg);
background-color: #2c3e50;
color: white;
border: none;
border-radius: 50%; /* Make the button circular */
@ -122,7 +48,7 @@ body {
}
.navbar-button:hover {
background-color: var(--button-hover-bg); /* Darken color on hover */
background-color: #0066cc; /* Darken color on hover */
z-index: 99;
}
@ -140,6 +66,20 @@ body {
bottom: 0;
}
#statusBar {
height: 3vh; /* Set the height of the status bar */
background-color: #333;
color: #ffffff;
display: flex;
align-items: center;
padding: 0 10px;
box-sizing: border-box;
}
#statusMessage {
margin: 0;
}
/* Styles for the upload-popup container */
.popup {
display: none; /* Hide initially */
@ -157,8 +97,7 @@ body {
/* Styles for the upload-popup content */
.popup-content {
background-color: #333;
color: #fefefe;
background-color: #fefefe;
margin: 15% auto; /* Center the upload-popup vertically and horizontally */
padding: 20px;
border: 1px solid #888;
@ -212,7 +151,7 @@ body {
}
.progress {
background-color: var(--progress-bg);
background-color: var(--line-border-fill);
position: absolute;
top: 50%;
left: 0;
@ -224,9 +163,10 @@ body {
}
.progress.error {
background-color: var(--error-color); /* Set to red color for error state */
background-color: red; /* Set to red color for error state */
}
.circle {
background-color: #fff;
color: #999;
@ -246,7 +186,7 @@ body {
}
.circle.error {
border-color: var(--error-color); /* Optionally, change border color to red */
border-color: red; /* Optionally, change border color to red */
color: white; /* Optionally, adjust text color for visibility */
}
@ -271,20 +211,15 @@ body {
}
.library-details {
flex: 1;
line-height: 5px;
line-height: 10px;
}
.library-details-cv {
color: #7a7a7a;
font-size: 15px;
}
.library-item {
cursor: pointer;
padding: 10px;
border-bottom: 1px solid #7f8c8d; /* Lighter border */
border-bottom: 1px solid #e9ecef;
}
.library-item.selected {
background-color: #34495e; /* Darker background for selected items */
background-color: #e9ecef;
}
.library-content {
flex: 1;
@ -293,27 +228,11 @@ body {
max-height: 720px; /* Ensure a max height for scrolling */
}
.library-item:hover {
background-color: #7f8c8d; /* Hover effect for items */
background-color: #cacaca;
}
.button-container {
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
}
.btn-invisible {
background: transparent;
border: none;
padding: 5px;
cursor: pointer;
}
.swipe-text {
font-size: 14px;
color: #ccc;
}
.pointer-pointer {
cursor: pointer;
justify-content: flex-end;
gap: 10px;
}

View File

@ -1,117 +0,0 @@
print("checking computer type...")
local machineType = 0
local isAdvanced = false
local isPocket = false
local isTurtle = false
local isCommand = false
-- Check if advanced (multishell exists in advanced computers and advanced pocket computers)
if multishell then
isAdvanced = true
end
-- Check if Pocket Computer (API specific to Pocket Computers)
if pocket then
isPocket = true
end
-- Check if Turtle (turtle API is available for turtles)
if turtle then
isTurtle = true
end
if commands then
isCommand = true
end
-- Determine machine type
if isTurtle then
if isAdvanced then
machineType = 3 -- Advanced Turtle
else
machineType = 1 -- Standard Turtle
end
elseif isPocket then
if isAdvanced then
machineType = 4 -- Advanced Pocket Computer
else
machineType = 2 -- Standard Pocket Computer
end
else
if isAdvanced then
if isCommand then
machineType = 7 -- Command Computer
else
machineType = 6 -- Advanced Computer
end
else
machineType = 5 -- Standard Computer
end
end
print("Machine Type: " .. machineType)
print("connecting to IDE...")
local ws = assert(http.websocket("ws://127.0.0.1:5133"))
print("connected to server")
local id
local isrunning = true
function exitcheck()
while true do
local event = os.pullEventRaw("terminate")
if event == "terminate" then
print("Exiting...")
isrunning = false
ws.close()
break
end
end
end
function main()
while isrunning do
print("ready")
local message, error = ws.receive()
if message then
print("Received message:", message)
if message == "ping" then
ws.send("pong")
elseif message == "info" then
info = {
OSVersion = os.version(),
ID = os.getComputerID(),
Name = os.getComputerLabel() or "No Name",
Type = machineType,
uptime = os.clock()
}
ws.send(textutils.serialiseJSON(info))
elseif message == "sendcode" then
local file = io.open("main.lua", "w")
print("waiting for code")
local filedata, error = ws.receive()
file:write(filedata)
file:close()
elseif message == "runcode" then
if isAdvanced then
id = multishell.launch({}, "main.lua")
multishell.setTitle(id, "Code")
multishell.setFocus(id)
else
shell.run("main")
end
elseif message == "exit" then
print("Exiting...")
break
end
else
print("WebSocket error:", error)
break
end
end
end
parallel.waitForAny(exitcheck, main)
print("Exited")

View File

@ -1,23 +0,0 @@
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
module.exports = {
mode: "production",
target: "electron-main", // For Electron main process
entry: "./src/index.js", // Adjust based on your main entry file
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
externals: [nodeExternals()], // Exclude node_modules
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: "assets", to: "assets" },
{ from: "blocks", to: "blocks" },
{ from: "src/styles.css", to: "styles.css" },
],
}),
],
};

View File

@ -1,18 +0,0 @@
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "production",
target: "web", // This should target the web environment for the renderer process
entry: "./src/frontend.js", // Ensure this points to the correct renderer entry
output: {
filename: "renderer.js",
path: path.resolve(__dirname, "dist"),
},
plugins: [
new HtmlWebpackPlugin({
template: "src/index.html", // Ensure this injects the renderer script
}),
],
};