~starkingdoms/starkingdoms

524ded0b2cb6532b3d1f382ea0c38ad5cca19096 — core 1 year, 8 months ago 53faad3 + 06481f6
merg
M server/src/component.rs => server/src/component.rs +3 -2
@@ 13,7 13,7 @@
//
//     You should have received a copy of the GNU Affero General Public License
//     along with this program.  If not, see <https://www.gnu.org/licenses/>.
use std::net::SocketAddr;
use std::{collections::HashMap, net::SocketAddr};

use bevy::prelude::*;
use serde::{Deserialize, Serialize};


@@ 26,7 26,7 @@ pub enum PlanetType {
    Mars,
}

#[derive(Component, Clone, Copy, PartialEq, Serialize, Deserialize, Debug)]
#[derive(Component, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
pub enum PartType {
    Placeholder,
    Hearty,


@@ 92,6 92,7 @@ pub struct Player {
    pub save_eligibility: bool,
    pub energy_capacity: u32,
    pub energy: u32,
    pub save_amount: HashMap<PartType, u32>, // PartType, amount of each module type
}

#[derive(Bundle)]

M server/src/main.rs => server/src/main.rs +44 -23
@@ 19,6 19,7 @@
#[global_allocator]
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;

use std::collections::HashMap;
use std::net::IpAddr;

use crate::mathutil::rot2d;


@@ 35,10 36,7 @@ use component::*;
use hmac::{Hmac, Mac};
use jwt::VerifyWithKey;
use packet::*;
use part::{
    HEARTY_CAPACITY, HEARTY_THRUST_ENERGY, LANDING_THRUSTER_CAPACITY, LANDING_THRUSTER_ENERGY,
    LANDING_THRUSTER_FORCE,
};
use part::{HEARTY_THRUST_ENERGY, LANDING_THRUSTER_ENERGY};
use rand::Rng;
use serde::{Deserialize, Serialize};
use sha2::Sha256;


@@ 364,22 362,21 @@ fn on_message(
                    let mut transform =
                        Transform::from_xyz(angle.cos() * 30.0, angle.sin() * 30.0, 0.0);
                    transform.rotate_z(angle);
                    let mut entity_id = commands.spawn((
                        PartBundle {
                            part_type: PartType::Hearty,
                            transform: TransformBundle::from(transform),
                            flags: PartFlags { attached: false },
                        },
                        Player {
                            addr: *from,
                            username: username.to_string(),
                            input: component::Input::default(),
                            selected: None,
                            save_eligibility: false,
                            energy_capacity: part::HEARTY_CAPACITY,
                            energy: part::HEARTY_CAPACITY,
                        },
                    ));
                    let mut player_comp = Player {
                        addr: *from,
                        username: username.to_string(),
                        input: component::Input::default(),
                        selected: None,
                        save_eligibility: false,
                        energy_capacity: part::HEARTY_CAPACITY,
                        energy: part::HEARTY_CAPACITY,
                        save_amount: HashMap::new(),
                    };
                    let mut entity_id = commands.spawn(PartBundle {
                        part_type: PartType::Hearty,
                        transform: TransformBundle::from(transform),
                        flags: PartFlags { attached: false },
                    });
                    entity_id
                        .insert(Collider::cuboid(0.5, 0.5))
                        .insert(AdditionalMassProperties::MassProperties(MassProperties {


@@ 418,6 415,7 @@ fn on_message(
                                &mut attached_query,
                                &mut part_query,
                                &mut player_query,
                                &mut player_comp,
                            );
                            attach.children = children;
                        } else {


@@ 435,6 433,7 @@ fn on_message(
                        // nothing to do
                    }
                    let mut entity_id = commands.entity(entity);
                    entity_id.insert(player_comp);
                    entity_id.insert(attach);

                    // tell this player the planets


@@ 739,13 738,26 @@ fn on_message(
                        }
                    }
                }
                Packet::RequestSave {} => {
                Packet::RequestSave { old_save } => {
                    for (_, q_player, _, _, attach, _) in &mut player_query {
                        if q_player.addr == *from {
                            // HEY! GHOSTLY! PLEASE FILL THIS STRUCT WITH DATA!
                            // THANKS!

                            let unused_modules = if let Some(ref old_save) = old_save {
                                if let Ok(old_savedata) =
                                    unpack_savefile(&app_keys.app_key, old_save.to_string())
                                {
                                    old_savedata.unused_modules
                                } else {
                                    Vec::new()
                                }
                            } else {
                                Vec::new()
                            };
                            let save = SaveData {
                                children: construct_save_data(attach.clone(), &attached_query),
                                unused_modules,
                            };
                            let save_string = pack_savefile(&app_keys.app_key, save);
                            let packet = Packet::SaveData {


@@ 809,6 821,7 @@ fn load_savefile(
        ),
        Without<PlanetType>,
    >,
    player_comp: &mut Player,
) -> [Option<Entity>; 4] {
    let mut ret = [None, None, None, None];
    for (i, child) in children.iter().enumerate() {


@@ 858,6 871,12 @@ fn load_savefile(
                module.id()
            };

            if let Some(amount) = player_comp.save_amount.get(&part_type) {
                player_comp.save_amount.insert(part_type, amount + 1);
            } else {
                player_comp.save_amount.insert(part_type, 1);
            }

            let children = if part_type != PartType::LandingThruster {
                load_savefile(
                    commands,


@@ 868,6 887,7 @@ fn load_savefile(
                    attached_query,
                    part_query,
                    player_query,
                    player_comp,
                )
            } else {
                [None, None, None, None]


@@ 982,8 1002,7 @@ fn load_savefile(
                    children: [None, None, Some(suspension_id), None],
                });
            }
            let mut player = player_query.get_mut(player_id).unwrap();
            player.1.energy_capacity += capacity!(part_type);
            player_comp.energy_capacity += capacity!(part_type);
        }
    }
    ret


@@ 1283,6 1302,8 @@ fn attach_on_module_tree(
        attach.children[attachment_slot] = Some(module.0);
        module.5.attached = true;
        player_query.get_mut(player_id).unwrap().1.energy_capacity += capacity!(*module.1);
        let save = player_query.get(player_id).unwrap().1.save_amount.clone();
        println!("{:?}", save);
        if *module.1 == PartType::LandingThruster {
            let loose_attach = module.4.unwrap().clone();
            let mut transform = part_query

M server/src/packet.rs => server/src/packet.rs +4 -1
@@ 16,6 16,7 @@ use std::fmt::{Display, Formatter};
//     along with this program.  If not, see <https://www.gnu.org/licenses/>.
use crate::component::{PartType, PlanetType};
use serde::{Deserialize, Serialize};
use starkingdoms_common::Savefile;
use tungstenite::Message;

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]


@@ 101,7 102,9 @@ pub enum Packet {
        released: bool,
        button: ButtonType,
    },
    RequestSave {},
    RequestSave {
        old_save: Option<String>,
    },
    _SpecialDisconnect {},
    // clientbound
    SpawnPlayer {

M starkingdoms-client/src/components/Chatbox.svelte => starkingdoms-client/src/components/Chatbox.svelte +3 -1
@@ 63,7 63,9 @@
        }
        let req_packet: Packet = {
          t: PacketType.RequestSave,
          c: {},
          c: {
            old_save: window.localStorage.getItem("save"),
          },
        };
        sendPacket(global.client!, req_packet);
      } else {

M starkingdoms-client/src/config.ts => starkingdoms-client/src/config.ts +3 -2
@@ 37,7 37,7 @@ async function fetchWithTimeout(resource: RequestInfo | URL, options = {}) {
}

export async function loadConfig(): Promise<Config> {
  logger("loading configuration from " + CONFIG_URL);
  /*logger("loading configuration from " + CONFIG_URL);
  try {
    const response = await fetchWithTimeout(CONFIG_URL, {
      timeout: 1000,


@@ 47,7 47,8 @@ export async function loadConfig(): Promise<Config> {
    logger(`error loading configuration: ${e}, using fallback`);
    // @ts-ignore strong types are unhelpful here
    return CONFIG;
  }
  }*/
  return CONFIG;
}

export const DEFAULT_CONFIG = CONFIG;

M starkingdoms-client/src/pages/Play.svelte => starkingdoms-client/src/pages/Play.svelte +3 -1
@@ 48,7 48,9 @@
    }
    let req_packet: Packet = {
      t: PacketType.RequestSave,
      c: {},
      c: {
        old_save: window.localStorage.getItem("save"),
      },
    };
    sendPacket(global.client!, req_packet);
    global.leaving = true;

M starkingdoms-client/src/pages/ShipEditor.svelte => starkingdoms-client/src/pages/ShipEditor.svelte +75 -33
@@ 19,6 19,7 @@
  })();

  let selected: PartType | null = null;
  let rotation: number | null = null;

  const logger = createDebug("main");
  logger(


@@ 89,14 90,14 @@
  let save = unpack_save(window.localStorage.getItem("save")!);
  console.log(save);

  let grid: Map<number, Map<number, PartType>> = new Map();
  let grid: Map<number, Map<number, [PartType, number]>> = new Map();

  function placePart(x: number, y: number, part: PartType) {
  function placePart(x: number, y: number, part: PartType, rotation: number) {
    if (!grid.has(x)) {
      grid.set(x, new Map());
    }

    grid.get(x)?.set(y, part);
    grid.get(x)?.set(y, [part, rotation]);
  }

  let textures: Map<PartType, HTMLImageElement> = new Map();


@@ 132,6 133,7 @@
    grid: Map<number, Map<number, PartType>>,
    grid_x: number,
    grid_y: number,
    rotation: number | null,
  ) {
    if (ctx === null) {
      return;


@@ 140,8 142,51 @@
    // clear out the canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    (async () => {
      if (selected !== null) {
        if (!textures.has(selected!)) {
          let promise = new Promise((resolve, reject) => {
            let img = new Image(grid_size, grid_size);
            img.onload = () => resolve(img);
            img.onerror = reject;
            img.src = part_texture_url(selected!, true);
          });

          let img = await promise;

          textures.set(part_type, <HTMLImageElement>img);
        }
        let canvas_x = grid_x * grid_size + x;
        let canvas_y = grid_y * grid_size + y;

        let img = textures.get(selected!)!;
        ctx.save();
        ctx.translate(canvas_x + grid_size / 2, canvas_y + grid_size / 2);
        ctx.rotate((rotation * Math.PI) / 2);
        ctx.globalAlpha = 0.2;
        ctx.drawImage(
          img,
          -grid_size / 2,
          -grid_size / 2,
          grid_size,
          grid_size,
        );
        ctx.globalCompositeOperation = "source-atop";
        if (selected === null) {
          ctx.fillStyle = "rgb(24, 24, 37)";
        } else if (canPlace()) {
          ctx.fillStyle = "rgb(166, 227, 161)";
        } else {
          ctx.fillStyle = "rgb(243, 139, 169)";
        }
        ctx.globalAlpha = 0.5;
        ctx.fillRect(-grid_size / 2, -grid_size / 2, grid_size, grid_size);
        ctx.restore();
      }
    })();

    grid.forEach((row, x_coord) => {
      row.forEach(async (part_type, y_coord) => {
      row.forEach(async ([part_type, rotation], y_coord) => {
        // draw the image
        if (!textures.has(part_type)) {
          let promise = new Promise((resolve, reject) => {


@@ 164,29 209,10 @@
        ctx.drawImage(img, canvas_x, canvas_y, grid_size, grid_size);
      });
    });

    ctx.beginPath();

    if (selected === null) {
      ctx.strokeStyle = "rgb(24, 24, 37)";
    } else if (canPlace()) {
      ctx.strokeStyle = "rgb(166, 227, 161)";
    } else {
      ctx.strokeStyle = "rgb(243, 139, 169)";
    }

    ctx.lineWidth = 2;
    ctx.strokeRect(
      grid_x * grid_size + x,
      grid_y * grid_size + y,
      grid_size,
      grid_size,
    );
    ctx.stroke();
  }

  $: {
    render(context, x, y, scale, grid, grid_x, grid_y);
    render(context, x, y, scale, grid, grid_x, grid_y, rotation);
  }

  onMount(() => {


@@ 208,14 234,14 @@
  let part_counts: Map<PartType, { used: number; available: number }> =
    new Map();

  function place(x: number, y: number, data: any) {
  function place(x: number, y: number, data: any, rotation: number) {
    if (data === null) {
      return;
    }

    let [type, children] = data;

    placePart(x, y, type);
    placePart(x, y, type, rotation);

    let existing_part_count =
      part_counts.get(<PartType>type) === undefined


@@ 228,22 254,22 @@
    });

    if (children[0] !== null) {
      place(x, y + 1, children[0]);
      place(x, y + 1, children[0], rotation);
    }
    if (children[1] !== null) {
      place(x + 1, y, children[1]);
      place(x + 1, y, children[1], (rotation + 1) % 4);
    }
    if (children[2] !== null) {
      place(x, y - 1, children[2]);
      place(x, y - 1, children[2], (rotation + 2) % 4);
    }
    if (children[3] !== null) {
      place(x - 1, y, children[3]);
      place(x - 1, y, children[3], (rotation + 3) % 4);
    }
  }

  function load_save_data(save: any) {
    grid = new Map();
    place(0, 0, [PartType.Hearty, save[0]]);
    place(0, 0, [PartType.Hearty, save[0]], 0);
  }

  $: {


@@ 285,9 311,12 @@
    console.log(type);
    if (selected == type) {
      selected = null;
      rotation = null;
    } else {
      selected = type;
      rotation = 0;
    }
    canvas.focus();
  }

  function mousedown(e: MouseEvent) {


@@ 296,7 325,7 @@
        if (!grid.has(grid_x)) {
          grid.set(grid_x, new Map());
        }
        grid.get(grid_x)!.set(grid_y, selected!);
        grid.get(grid_x)!.set(grid_y, [selected!, rotation]);
        part_counts.set(selected!, {
          used: part_counts.get(selected!)!.used + 1,
          available: part_counts.get(selected!)!.available,


@@ 312,7 341,7 @@
    } else if (e.button == 2) {
      // removal
      if (grid.get(grid_x)?.has(grid_y)) {
        let type = grid.get(grid_x)!.get(grid_y)!;
        let type = grid.get(grid_x)!.get(grid_y)[0]!;
        if (type === PartType.Hearty) {
          return;
        }


@@ 325,6 354,17 @@
        grid = grid;
      }
    }
    canvas.focus();
  }

  function keydown(e: KeyboardEvent) {
    console.log("here");
    if (e.code == "KeyQ") {
      rotation = (rotation - 1) % 4;
    }
    if (e.code == "KeyE") {
      rotation = (rotation + 1) % 4;
    }
  }
</script>



@@ 407,10 447,12 @@
  on:mouseup|preventDefault={mouseup}
  on:mousemove|preventDefault={mousemove}
  on:wheel|preventDefault={handleWheel}
  on:keydown={keydown}
  on:contextmenu|preventDefault={() => {
    return false;
  }}
  style="background-size: {grid_size}px {grid_size}px; background-position: {x}px {y}px;"
  tabindex="1"
  bind:this={canvas} />

<span style="position: absolute; top: 10px; right: 10px;">

M starkingdoms-client/src/protocol.ts => starkingdoms-client/src/protocol.ts +3 -1
@@ 88,7 88,9 @@ export interface MessagePacket {
  actor: string;
  content: string;
}
export interface RequestSavePacket {}
export interface RequestSavePacket {
  old_save: string | null;
}
export interface SaveEligibilityPacket {
  eligible: boolean;
}

M starkingdoms-common/src/lib.rs => starkingdoms-common/src/lib.rs +6 -5
@@ 18,9 18,9 @@ use base64::Engine;
use hmac::{Hmac, Mac};
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use std::error::Error;
use std::{collections::HashMap, error::Error};

#[derive(Clone, Serialize, Deserialize, Debug)]
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
pub struct SaveData {
    // ----------------------------------------------------------------------
    // HEY YOU


@@ 30,15 30,16 @@ pub struct SaveData {
    // ----------------------------------------------------------------------
    // THANKS! -core
    pub children: Vec<Option<SaveModule>>,
    pub unused_modules: Vec<(PartType, u32)>,
}

#[derive(Clone, Serialize, Deserialize, Debug)]
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
pub struct SaveModule {
    pub part_type: PartType,
    pub children: Vec<Option<SaveModule>>,
}

#[derive(Clone, Copy, PartialEq, Serialize, Deserialize, Debug)]
#[derive(Clone, Copy, PartialEq, Hash, Eq, Serialize, Deserialize, Debug)]
pub enum PartType {
    Placeholder,
    Hearty,


@@ 49,7 50,7 @@ pub enum PartType {
}

// no touchy. this is the struct that savefiles are actually represented in
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct Savefile {
    data_msgpack: Vec<u8>,
    mac: Vec<u8>,