This commit is contained in:
Masashi 2025-12-17 21:02:34 +05:30
commit 77a32b1d21
9 changed files with 508 additions and 63 deletions

144
soup/client.c Normal file
View file

@ -0,0 +1,144 @@
#include "libsuicmez/libsuicmez.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
void* gc_alloc(const TypeInfo* type, size_t size);
void gc_init(void);
void gc_shutdown(void);
// Helper for allocating arrays
static void* suic_alloc_array(const TypeInfo* type, size_t elem_size, size_t len, void* init_data) {
void* ptr = gc_alloc(type, elem_size * len);
if (init_data) memcpy(ptr, init_data, elem_size * len);
return ptr;
}
// Helper for allocating structs
static void* suic_alloc_struct(const TypeInfo* type, size_t size, void* init_data) {
void* ptr = gc_alloc(type, size);
if (init_data) memcpy(ptr, init_data, size);
return ptr;
}
int suic_main(void);
int suic_main(void) {
char* GAME_NAME = suic_alloc_array(NULL, sizeof(char), 5, "Soup");
char* GAME_VERSION = suic_alloc_array(NULL, sizeof(char), 6, "0.1.0");
int WINDOW_WIDTH = 1280;
int WINDOW_HEIGHT = 720;
char* WINDOW_TITLE = suic_alloc_array(NULL, sizeof(char), 27, "Soup - Voxel Battle Royale");
char* SERVER_HOST = suic_alloc_array(NULL, sizeof(char), 10, "127.0.0.1");
int SERVER_PORT = 8888;
suic_init_window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE);
suic_enable_msaa_4x();
int current_screen = 0;
char* player_name = suic_alloc_array(NULL, sizeof(char), 7, "Player");
char* server_host = SERVER_HOST;
int server_port = SERVER_PORT;
suic_show_fps_meter((suic_vec2){ .x = 10.000000, .y = 10.000000 });
while ((suic_window_should_close() == false)) {
suic_begin_drawing();
suic_clear_background(30, 30, 40, 255);
if ((current_screen == 0)) {
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 5, "SOUP"), 450, 80, 120, 100, 200, 255, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 20, "Voxel Battle Royale"), 400, 220, 40, 200, 200, 200, 255);
suic_draw_rectangle(450, 320, 380, 80, 50, 150, 255, 255);
suic_draw_rectangle_lines(450, 320, 380, 80, 100, 200, 255, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 5, "PLAY"), 600, 350, 40, 255, 255, 255, 255);
suic_draw_rectangle(450, 430, 380, 80, 100, 100, 100, 255);
suic_draw_rectangle_lines(450, 430, 380, 80, 150, 150, 150, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 9, "SETTINGS"), 540, 460, 40, 255, 255, 255, 255);
suic_draw_rectangle(450, 540, 380, 80, 150, 50, 50, 255);
suic_draw_rectangle_lines(450, 540, 380, 80, 200, 100, 100, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 5, "QUIT"), 600, 570, 40, 255, 255, 255, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 22, "v0.1.0 - Early Access"), 500, 680, 20, 150, 150, 150, 255);
int mouse_x = (int) suic_get_mouse_x();
int mouse_y = (int) suic_get_mouse_y();
int is_mouse_clicked = suic_is_mouse_button_pressed(MOUSE_BUTTON_LEFT);
if (((((mouse_x >= 450) && (mouse_x <= 830)) && (mouse_y >= 320)) && (mouse_y <= 400))) {
if ((is_mouse_clicked != 0)) {
current_screen = 1;
}
}
if (((((mouse_x >= 450) && (mouse_x <= 830)) && (mouse_y >= 430)) && (mouse_y <= 510))) {
if ((is_mouse_clicked != 0)) {
current_screen = 2;
}
}
if (((((mouse_x >= 450) && (mouse_x <= 830)) && (mouse_y >= 540)) && (mouse_y <= 620))) {
if ((is_mouse_clicked != 0)) {
break;
}
}
} else {
if ((current_screen == 1)) {
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 12, "MATCHMAKING"), 450, 100, 60, 100, 200, 255, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 24, "Connecting to server..."), 400, 250, 30, 200, 200, 200, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 21, "Host: 127.0.0.1:8888"), 400, 310, 20, 150, 150, 150, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 18, "Your Name: Player"), 400, 380, 20, 200, 200, 200, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 24, "Players in Queue: 0/100"), 400, 420, 20, 200, 200, 200, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 11, "Loading..."), 550, 500, 30, 100, 200, 255, 255);
suic_draw_rectangle(100, 630, 150, 60, 150, 50, 50, 255);
suic_draw_rectangle_lines(100, 630, 150, 60, 200, 100, 100, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 5, "BACK"), 120, 650, 30, 255, 255, 255, 255);
int mouse_x = (int) suic_get_mouse_x();
int mouse_y = (int) suic_get_mouse_y();
int is_mouse_clicked = suic_is_mouse_button_pressed(MOUSE_BUTTON_LEFT);
if (((((mouse_x >= 100) && (mouse_x <= 250)) && (mouse_y >= 630)) && (mouse_y <= 690))) {
if ((is_mouse_clicked != 0)) {
current_screen = 0;
}
}
} else {
if ((current_screen == 2)) {
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 9, "SETTINGS"), 450, 100, 60, 100, 200, 255, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 13, "Player Name:"), 250, 200, 24, 200, 200, 200, 255);
suic_draw_rectangle(450, 190, 300, 50, 50, 50, 50, 255);
suic_draw_rectangle_lines(450, 190, 300, 50, 100, 100, 100, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 7, "Player"), 460, 205, 20, 255, 255, 255, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 13, "Server Host:"), 250, 300, 24, 200, 200, 200, 255);
suic_draw_rectangle(450, 290, 300, 50, 50, 50, 50, 255);
suic_draw_rectangle_lines(450, 290, 300, 50, 100, 100, 100, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 10, "127.0.0.1"), 460, 305, 20, 255, 255, 255, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 13, "Server Port:"), 250, 400, 24, 200, 200, 200, 255);
suic_draw_rectangle(450, 390, 300, 50, 50, 50, 50, 255);
suic_draw_rectangle_lines(450, 390, 300, 50, 100, 100, 100, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 5, "8888"), 460, 405, 20, 255, 255, 255, 255);
suic_draw_rectangle(450, 550, 300, 70, 100, 100, 100, 255);
suic_draw_rectangle_lines(450, 550, 300, 70, 150, 150, 150, 255);
suic_draw_text(suic_alloc_array(NULL, sizeof(char), 13, "BACK TO HOME"), 495, 577, 30, 255, 255, 255, 255);
int mouse_x = (int) suic_get_mouse_x();
int mouse_y = (int) suic_get_mouse_y();
int is_mouse_clicked = suic_is_mouse_button_pressed(MOUSE_BUTTON_LEFT);
if (((((mouse_x >= 450) && (mouse_x <= 750)) && (mouse_y >= 550)) && (mouse_y <= 620))) {
if ((is_mouse_clicked != 0)) {
current_screen = 0;
}
}
}
}
}
suic_end_drawing();
}
suic_close_window();
return 0;
}
int main(int argc, char* argv[]) {
// Initialize GC
gc_init();
// init globals
// init event loop
int result = suic_main();
// Shutdown GC
gc_shutdown();
return result;
}

BIN
soup/client.o Executable file

Binary file not shown.

152
soup/client.sui Normal file
View file

@ -0,0 +1,152 @@
# Soup - Client
# Voxel Battle Royale Client
# Main game UI and home page with menu
fn main() -> int do
# Initialize graphics
init_window(1280, 720, "Soup - Voxel Battle Royale")
defer close_window()
enable_msaa_4x()
# Game state
let mut current_screen = 0 # 0 = Home, 1 = Lobby, 2 = Game
let mut player_name = "Player"
let mut server_host = "127.0.0.1"
let mut server_port = 8888
# Show FPS meter
show_fps_meter(Vec2 { x: 10.0, y: 10.0 })
# Main loop
while window_should_close() == false do
begin_drawing()
clear_background(30, 30, 40, 255) # Dark blue background
if current_screen == 0 do
# ===== HOME SCREEN =====
# Title
draw_text("SOUP", 450, 80, 120, 100, 200, 255, 255) # Cyan title
draw_text("Voxel Battle Royale", 400, 220, 40, 200, 200, 200, 255) # Subtitle
# Main buttons
# Play button
draw_rectangle(450, 320, 380, 80, 50, 150, 255, 255) # Blue button
draw_rectangle_lines(450, 320, 380, 80, 100, 200, 255, 255) # Border
draw_text("PLAY", 600, 350, 40, 255, 255, 255, 255)
# Settings button
draw_rectangle(450, 430, 380, 80, 100, 100, 100, 255) # Gray button
draw_rectangle_lines(450, 430, 380, 80, 150, 150, 150, 255)
draw_text("SETTINGS", 540, 460, 40, 255, 255, 255, 255)
# Quit button
draw_rectangle(450, 540, 380, 80, 150, 50, 50, 255) # Red button
draw_rectangle_lines(450, 540, 380, 80, 200, 100, 100, 255)
draw_text("QUIT", 600, 570, 40, 255, 255, 255, 255)
# Footer
draw_text("v0.1.0 - Early Access", 500, 680, 20, 150, 150, 150, 255)
# Check for button clicks
let mouse_x = get_mouse_x() as int
let mouse_y = get_mouse_y() as int
let is_mouse_clicked = is_mouse_button_pressed(MOUSE_BUTTON_LEFT)
# Play button click
if mouse_x >= 450 and mouse_x <= 830 and mouse_y >= 320 and mouse_y <= 400 do
if is_mouse_clicked != 0 do
current_screen = 1 # Go to lobby
end
end
# Settings button click
if mouse_x >= 450 and mouse_x <= 830 and mouse_y >= 430 and mouse_y <= 510 do
if is_mouse_clicked != 0 do
current_screen = 2 # Go to settings
end
end
# Quit button click
if mouse_x >= 450 and mouse_x <= 830 and mouse_y >= 540 and mouse_y <= 620 do
if is_mouse_clicked != 0 do
break
end
end
end else if current_screen == 1 do
# ===== LOBBY SCREEN =====
draw_text("MATCHMAKING", 450, 100, 60, 100, 200, 255, 255)
# Status message
draw_text("Connecting to server...", 400, 250, 30, 200, 200, 200, 255)
draw_text("Host: 127.0.0.1:8888", 400, 310, 20, 150, 150, 150, 255)
# Player info
draw_text("Your Name: Player", 400, 380, 20, 200, 200, 200, 255)
draw_text("Players in Queue: 0/100", 400, 420, 20, 200, 200, 200, 255)
# Loading animation (simple dots)
draw_text("Loading...", 550, 500, 30, 100, 200, 255, 255)
# Back button
draw_rectangle(100, 630, 150, 60, 150, 50, 50, 255)
draw_rectangle_lines(100, 630, 150, 60, 200, 100, 100, 255)
draw_text("BACK", 120, 650, 30, 255, 255, 255, 255)
# Cancel button
let mouse_x = get_mouse_x() as int
let mouse_y = get_mouse_y() as int
let is_mouse_clicked = is_mouse_button_pressed(MOUSE_BUTTON_LEFT)
if mouse_x >= 100 and mouse_x <= 250 and mouse_y >= 630 and mouse_y <= 690 do
if is_mouse_clicked != 0 do
current_screen = 0 # Back to home
end
end
end else if current_screen == 2 do
# ===== SETTINGS SCREEN =====
draw_text("SETTINGS", 450, 100, 60, 100, 200, 255, 255)
# Settings options
draw_text("Player Name:", 250, 200, 24, 200, 200, 200, 255)
draw_rectangle(450, 190, 300, 50, 50, 50, 50, 255)
draw_rectangle_lines(450, 190, 300, 50, 100, 100, 100, 255)
draw_text("Player", 460, 205, 20, 255, 255, 255, 255)
draw_text("Server Host:", 250, 300, 24, 200, 200, 200, 255)
draw_rectangle(450, 290, 300, 50, 50, 50, 50, 255)
draw_rectangle_lines(450, 290, 300, 50, 100, 100, 100, 255)
draw_text("127.0.0.1", 460, 305, 20, 255, 255, 255, 255)
draw_text("Server Port:", 250, 400, 24, 200, 200, 200, 255)
draw_rectangle(450, 390, 300, 50, 50, 50, 50, 255)
draw_rectangle_lines(450, 390, 300, 50, 100, 100, 100, 255)
draw_text("8888", 460, 405, 20, 255, 255, 255, 255)
# Back button
draw_rectangle(450, 550, 300, 70, 100, 100, 100, 255)
draw_rectangle_lines(450, 550, 300, 70, 150, 150, 150, 255)
draw_text("BACK TO HOME", 495, 577, 30, 255, 255, 255, 255)
let mouse_x = get_mouse_x() as int
let mouse_y = get_mouse_y() as int
let is_mouse_clicked = is_mouse_button_pressed(MOUSE_BUTTON_LEFT)
if mouse_x >= 450 and mouse_x <= 750 and mouse_y >= 550 and mouse_y <= 620 do
if is_mouse_clicked != 0 do
current_screen = 0 # Back to home
end
end
end
end_drawing()
end
0
end

View file

@ -1,61 +0,0 @@
# ===== GAME SETTINGS =====
let GAME_NAME = "Soup"
let GAME_VERSION = "0.1.0"
# ===== WINDOW/GRAPHICS =====
let WINDOW_WIDTH = 1280
let WINDOW_HEIGHT = 720
let WINDOW_TITLE = "Soup - Voxel Battle Royale"
let TARGET_FPS = 60
let USE_MSAA_4X = 1
# ===== WORLD SETTINGS =====
# Island generation
let ISLAND_SIZE_X = 512
let ISLAND_SIZE_Z = 512
let ISLAND_MAX_HEIGHT = 128
let ISLAND_VOXEL_SIZE = 1.0 # Size of each voxel in world units
# Island generation seed (0 = random)
let ISLAND_SCALE = 50.0 # Noise scale for generation
let ISLAND_PERSISTENCE = 0.5
let ISLAND_LACUNARITY = 2.0
let ISLAND_OCTAVES = 6
# ===== PLAYER SETTINGS =====
let PLAYER_MOVE_SPEED = 5.0
let PLAYER_SPRINT_SPEED = 8.0
let PLAYER_JUMP_FORCE = 8.0
let PLAYER_HEIGHT = 1.6
let PLAYER_WIDTH = 0.8
let PLAYER_MASS = 1.0
let PLAYER_MOUSE_SENSITIVITY = 0.003
# ===== NETWORKING SETTINGS =====
let SERVER_HOST = "127.0.0.1"
let SERVER_PORT = 8888
let MAX_PLAYERS = 100
let MAX_CONNECTIONS = 128
let NETWORK_TICK_RATE = 20 # Updates per second
let NETWORK_TIMEOUT_MS = 30000 # 30 seconds
# Message buffer sizes
let MAX_MESSAGE_SIZE = 4096
let RECV_BUFFER_SIZE = 65536
# ===== BATTLE ROYALE SETTINGS =====
let MATCH_DURATION_SECONDS = 600 # 10 minutes
let ZONE_SHRINK_START = 120 # Seconds before first zone shrink
let ZONE_SHRINK_INTERVAL = 60 # Seconds between zone shrinks
let INITIAL_SAFE_ZONE_RADIUS = 200.0
let FINAL_SAFE_ZONE_RADIUS = 20.0
# ===== PHYSICS SETTINGS =====
let GRAVITY = -9.81
let GROUND_FRICTION = 0.5
# ===== DEBUG SETTINGS =====
let DEBUG_MODE = 1
let SHOW_FPS_METER = 1
let SHOW_PHYSICS_DEBUG = 0
let SHOW_NETWORK_DEBUG = 0

81
soup/server.c Normal file
View file

@ -0,0 +1,81 @@
#include "libsuicmez/libsuicmez.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
void* gc_alloc(const TypeInfo* type, size_t size);
void gc_init(void);
void gc_shutdown(void);
// Helper for allocating arrays
static void* suic_alloc_array(const TypeInfo* type, size_t elem_size, size_t len, void* init_data) {
void* ptr = gc_alloc(type, elem_size * len);
if (init_data) memcpy(ptr, init_data, elem_size * len);
return ptr;
}
// Helper for allocating structs
static void* suic_alloc_struct(const TypeInfo* type, size_t size, void* init_data) {
void* ptr = gc_alloc(type, size);
if (init_data) memcpy(ptr, init_data, size);
return ptr;
}
int suic_main(void);
int suic_main(void) {
char* SERVER_HOST = suic_alloc_array(NULL, sizeof(char), 10, "127.0.0.1");
int SERVER_PORT = 8888;
int MAX_PLAYERS = 100;
int MAX_CONNECTIONS = 128;
int NETWORK_TICK_RATE = 20;
int NETWORK_TIMEOUT_MS = 30000;
int MATCH_DURATION_SECONDS = 600;
int ZONE_SHRINK_START = 120;
int ZONE_SHRINK_INTERVAL = 60;
float INITIAL_SAFE_ZONE_RADIUS = 200.000000;
float FINAL_SAFE_ZONE_RADIUS = 20.000000;
int ISLAND_SIZE_X = 512;
int ISLAND_SIZE_Z = 512;
int ISLAND_MAX_HEIGHT = 128;
float ISLAND_SCALE = 50.000000;
int ISLAND_OCTAVES = 6;
suic_ode_init();
void* world = suic_ode_world_create();
void* null_space = NULL;
void* space = suic_ode_simple_space_create(null_space);
suic_ode_world_set_gravity(world, 0.000000, -9.810000, 0.000000);
void* contactgroup = suic_ode_joint_group_create(0);
void* ground_geom = suic_ode_create_plane_geom(space, 0.000000, 1.000000, 0.000000, 0.000000);
int tick = 0;
float tick_interval = (1.000000 / 20.000000);
while (true) {
tick = (tick + 1);
suic_ode_space_collide(world, space, contactgroup);
suic_ode_world_step(world, tick_interval);
suic_ode_joint_group_empty(contactgroup);
}
suic_ode_geom_destroy(ground_geom);
suic_ode_joint_group_destroy(contactgroup);
suic_ode_space_destroy(space);
suic_ode_world_destroy(world);
suic_ode_close();
return 0;
}
int main(int argc, char* argv[]) {
// Initialize GC
gc_init();
// init globals
// init event loop
int result = suic_main();
// Shutdown GC
gc_shutdown();
return result;
}

BIN
soup/server.o Executable file

Binary file not shown.

67
soup/server.sui Normal file
View file

@ -0,0 +1,67 @@
# Soup - Server
# Voxel Battle Royale Server
# Handles game logic, physics, and player synchronization
fn main() -> int do
# Server configuration
let SERVER_HOST = "127.0.0.1"
let SERVER_PORT = 8888
let MAX_PLAYERS = 100
let MAX_CONNECTIONS = 128
let NETWORK_TICK_RATE = 20
let MATCH_DURATION_SECONDS = 600
let ZONE_SHRINK_START = 120
let ZONE_SHRINK_INTERVAL = 60
let INITIAL_SAFE_ZONE_RADIUS = 200.0
let FINAL_SAFE_ZONE_RADIUS = 20.0
let ISLAND_SIZE_X = 512
let ISLAND_SIZE_Z = 512
let ISLAND_MAX_HEIGHT = 128
let ISLAND_SCALE = 50.0
let ISLAND_OCTAVES = 6
# Initialize ODE physics for server-side validation
ode_init()
defer ode_close()
let world = ode_world_create()
defer ode_world_destroy(world)
let null_space = 0 as *()
let space = ode_simple_space_create(null_space)
defer ode_space_destroy(space)
# Set gravity for physics validation
ode_world_set_gravity(world, 0.0, -9.81, 0.0)
let contactgroup = ode_joint_group_create(0)
defer ode_joint_group_destroy(contactgroup)
# Create ground plane for server-side collision checking
let ground_geom = ode_create_plane_geom(space, 0.0, 1.0, 0.0, 0.0)
defer ode_geom_destroy(ground_geom)
# Main server loop
let mut tick = 0
let tick_interval = 1.0 / 20.0 # NETWORK_TICK_RATE = 20
while true do
# Simulate game ticks
tick = tick + 1
# Physics update for validation
ode_space_collide(world, space, contactgroup)
ode_world_step(world, tick_interval)
ode_joint_group_empty(contactgroup)
# In a real implementation, this would:
# - Handle incoming player connections
# - Process player input messages
# - Update game state and physics
# - Broadcast state to all connected clients
# - Handle zone shrinking for battle royale
# - Check for player elimination
# - Manage match lifecycle
end
0
end

View file

@ -597,8 +597,9 @@ impl Transpiler {
"window_should_close" => "suic_window_should_close",
"set_target_fps" => "suic_set_target_fps",
"enable_msaa_4x" => "suic_enable_msaa_4x",
"get_frame_time" => "suic_get_frame_time",
"show_fps_meter" => "suic_show_fps_meter",
"get_frame_time" => "suic_get_frame_time",
"get_time" => "suic_get_time",
"show_fps_meter" => "suic_show_fps_meter",
"hide_fps_meter" => "suic_hide_fps_meter",
"begin_drawing" => "suic_begin_drawing",
"end_drawing" => "suic_end_drawing",

View file

@ -676,6 +676,14 @@ impl TypeChecker {
return_type: Type::Float,
},
);
self.env.functions.insert(
"get_time".to_string(),
FunctionType {
type_params: vec![],
params: vec![],
return_type: Type::Float,
},
);
self.env.functions.insert(
"show_fps_meter".to_string(),
FunctionType {
@ -780,6 +788,35 @@ impl TypeChecker {
);
}
// Mouse button constants
let mouse_buttons = vec![
"MOUSE_BUTTON_LEFT",
"MOUSE_BUTTON_RIGHT",
"MOUSE_BUTTON_MIDDLE",
"MOUSE_BUTTON_SIDE",
"MOUSE_BUTTON_EXTRA",
"MOUSE_BUTTON_FORWARD",
"MOUSE_BUTTON_BACK",
];
for (i, button) in mouse_buttons.iter().enumerate() {
let id = 2000 + i;
self.env.name_to_id.insert(button.to_string(), BindingId(id));
self.env.vars.insert(
BindingId(id),
VarInfo {
ty: Type::Int,
kind: BindingKind::Default,
name: button.to_string(),
usage: 0,
span: Span {
start: 0,
end: 0,
file: "builtin".to_string(),
},
},
);
}
// Drawing
self.env.functions.insert(
"begin_drawing".to_string(),
@ -978,6 +1015,30 @@ impl TypeChecker {
return_type: Type::Float,
},
);
self.env.functions.insert(
"is_mouse_button_pressed".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int], // mouse button
return_type: Type::Int,
},
);
self.env.functions.insert(
"is_mouse_button_down".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int], // mouse button
return_type: Type::Int,
},
);
self.env.functions.insert(
"is_mouse_button_released".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int], // mouse button
return_type: Type::Int,
},
);
// Math functions
self.env.functions.insert(