public override void NodeGUI() { UnityEditor.EditorGUIUtility.labelWidth = 100; GUILayout.BeginHorizontal(); GUILayout.BeginVertical(); Inputs [0].DisplayLayout(); GUILayout.EndVertical(); GUILayout.BeginVertical(); Outputs [0].DisplayLayout(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); type = (WarpType)UnityEditor.EditorGUILayout.EnumPopup( new GUIContent("CSG Operation", "The type of warp performed on Input 1"), type); strength = UnityEditor.EditorGUILayout.Vector3Field("Strength", strength); if (GUI.changed) { NodeEditor.RecalculateFrom(this); } }
public Alarm(String title, String desc, double ut, double time, string id = "", WarpType warp = WarpType.DoNothing, AlarmType alarmType = AlarmType.AlarmClockApp) { Title = title; Description = desc?.Replace("\n", "<br>"); UT = ut; Time = time; ID = id; Warp = warp; AlarmType = alarmType; }
// Draw an event tile. private void DrawEventTile(Graphics2D g, EventTileDataInstance eventTile, Point2I position, Color drawColor) { SpriteAnimation spr = eventTile.CurrentSprite; int imageVariantID = eventTile.Properties.GetInteger("image_variant"); if (imageVariantID < 0) { imageVariantID = eventTile.Room.Zone.ImageVariantID; } // Select different sprites for certain events. if (eventTile.Type == typeof(NPCEvent)) { eventTile.SubStripIndex = eventTile.Properties.GetInteger("direction", 0); } else if (eventTile.Type == typeof(WarpEvent)) { string warpTypeStr = eventTile.Properties.GetString("warp_type", "tunnel"); WarpType warpType = (WarpType)Enum.Parse(typeof(WarpType), warpTypeStr, true); if (warpType == WarpType.Entrance) { spr = GameData.SPR_EVENT_TILE_WARP_ENTRANCE; } else if (warpType == WarpType.Tunnel) { spr = GameData.SPR_EVENT_TILE_WARP_TUNNEL; } else if (warpType == WarpType.Stairs) { spr = GameData.SPR_EVENT_TILE_WARP_STAIRS; } } // Draw the sprite. if (!spr.IsNull) { g.DrawAnimation(spr, imageVariantID, editorControl.Ticks, position, drawColor); } else { Rectangle2I r = new Rectangle2I(position, eventTile.Size * GameSettings.TILE_SIZE); g.FillRectangle(r, Color.Blue); } }
//----------------------------------------------------------------------------- // Overridden methods //----------------------------------------------------------------------------- protected override void Initialize() { base.Initialize(); string typeName = Properties.GetString("warp_type", "Tunnel"); warpType = (WarpType)Enum.Parse(typeof(WarpType), typeName, true); collisionBox = new Rectangle2I(2, 6, 12, 12); warpEnabled = !IsTouchingPlayer(); // Find the closest room edge. edgeDirection = -1; Rectangle2I roomBounds = RoomControl.RoomBounds; Rectangle2I myBox = new Rectangle2I((int)position.X, (int)position.Y, 16, 16); int minDist = -1; for (int dir = 0; dir < 4; dir++) { int dist = Math.Abs(myBox.GetEdge(dir) - roomBounds.GetEdge(dir)); if (dist < minDist || minDist < 0) { edgeDirection = dir; minDist = dist; } } // Make sure we know if the player respawns on top of this warp point. RoomControl.PlayerRespawn += delegate(Player player) { warpEnabled = !IsTouchingPlayer(); }; // For entrance warp points, intercept room transitions in order to warp. RoomControl.RoomTransitioning += delegate(int direction) { if (warpType == WarpType.Entrance && direction == edgeDirection && IsTouchingPlayer()) { RoomControl.CancelRoomTransition(); Warp(direction); } }; }
public Warp(byte[] data) { this.Position = new Vector2(data[0], data[1]); Type = GetWarpType(data[2]); switch (Type) { case WarpType.Entrance: ObjectText = "E"; break; case WarpType.Next: ObjectText = "N"; break; case WarpType.Exit: ObjectText = "X"; break; default: break; } }
public void run() { try { for (;;) { if (!this.client.connectionAlive) { this.client.forceDisconnect(direction, "Connection no longer alive"); return; } if (this.client.kickTargetTimestamp != 0) { if (this.client.kickTargetTimestamp < Utils.getTimestamp()) { this.client.closeConnection(); return; } continue; } #region Process Packet //Packet ID and Vaildity Check. uint temp = this.incoming.ReadVarUInt32(); if (temp < 0 || temp > 48) { this.client.forceDisconnect(direction, "Sent invalid packet ID [" + temp + "]."); return; } Packet packetID = (Packet)temp; //Packet Size and Compression Check. bool compressed = false; int packetSize = this.incoming.ReadVarInt32(); if (packetSize < 0) { packetSize = -packetSize; compressed = true; } //Create buffer for forwarding byte[] dataBuffer = this.incoming.ReadFully(packetSize); //Do decompression MemoryStream ms = new MemoryStream(); if (compressed) { ZlibStream compressedStream = new ZlibStream(new MemoryStream(dataBuffer), CompressionMode.Decompress); byte[] buffer = new byte[32768]; for (;;) { int read = compressedStream.Read(buffer, 0, buffer.Length); if (read <= 0) { break; } ms.Write(buffer, 0, read); } ms.Seek(0, SeekOrigin.Begin); } else { ms = new MemoryStream(dataBuffer); } //Create packet parser BinaryReader packetData = new BinaryReader(ms); #endregion //Return data for packet processor object returnData = true; if (packetID != Packet.Heartbeat && packetID != Packet.UniverseTimeUpdate) { if (direction == Direction.Client) #region Handle Client Packets { #region Protocol State Security ClientState curState = this.client.state; if (curState != ClientState.Connected) { if (curState == ClientState.PendingConnect && packetID != Packet.ClientConnect) { this.client.rejectPreConnected("Violated PendingConnect protocol state with " + packetID); return; } else if (curState == ClientState.PendingAuthentication && packetID != Packet.HandshakeResponse) { this.client.rejectPreConnected("Violated PendingAuthentication protocol state with " + packetID); return; } else if (curState == ClientState.PendingConnectResponse) { int startTime = Utils.getTimestamp(); while (true) { if (this.client.state == ClientState.Connected) { break; } if (Utils.getTimestamp() > startTime + StarryboundServer.config.connectTimeout) { this.client.rejectPreConnected("Connection Failed: Server did not respond in time."); return; } } } } #endregion if (packetID == Packet.ChatSend) { returnData = new Packet11ChatSend(this.client, packetData, this.direction).onReceive(); } else if (packetID == Packet.ClientConnect) { this.client.state = ClientState.PendingAuthentication; returnData = new Packet7ClientConnect(this.client, packetData, this.direction).onReceive(); MemoryStream packet = new MemoryStream(); BinaryWriter packetWrite = new BinaryWriter(packet); passwordSalt = Utils.GenerateSecureSalt(); packetWrite.WriteStarString(""); packetWrite.WriteStarString(passwordSalt); packetWrite.WriteBE(StarryboundServer.config.passwordRounds); this.client.sendClientPacket(Packet.HandshakeChallenge, packet.ToArray()); } else if (packetID == Packet.HandshakeResponse) { string claimResponse = packetData.ReadStarString(); string passwordHash = packetData.ReadStarString(); string verifyHash = Utils.StarHashPassword(StarryboundServer.config.proxyPass, this.client.playerData.account + passwordSalt, StarryboundServer.config.passwordRounds); if (passwordHash != verifyHash) { this.client.rejectPreConnected("Your password was incorrect."); return; } this.client.state = ClientState.PendingConnectResponse; returnData = false; } else if (packetID == Packet.WarpCommand) { WarpType cmd = (WarpType)packetData.ReadUInt32BE(); WorldCoordinate coord = packetData.ReadStarWorldCoordinate(); string player = packetData.ReadStarString(); if (cmd == WarpType.WarpToPlayerShip) { Client target = StarryboundServer.getClient(player); if (target != null) { if (!this.client.playerData.canAccessShip(target.playerData)) { this.client.sendChatMessage("^#5dc4f4;You cannot access this player's ship due to their ship access settings."); StarryboundServer.logDebug("ShipAccess", "Preventing " + this.client.playerData.name + " from accessing " + target.playerData.name + "'s ship."); MemoryStream packetWarp = new MemoryStream(); BinaryWriter packetWrite = new BinaryWriter(packetWarp); packetWrite.WriteBE((uint)WarpType.WarpToOwnShip); packetWrite.Write(new WorldCoordinate()); packetWrite.WriteStarString(""); client.sendServerPacket(Packet.WarpCommand, packetWarp.ToArray()); returnData = false; } else { this.client.playerData.inPlayerShip = true; } } } else if (cmd == WarpType.WarpToOwnShip) { this.client.playerData.inPlayerShip = true; } else { this.client.playerData.inPlayerShip = false; } StarryboundServer.logDebug("WarpCommand", "[" + this.client.playerData.client + "][" + cmd + "]" + (coord != null ? "[" + coord.ToString() + "]" : "") + "[" + player + "] inPlayerShip:" + this.client.playerData.inPlayerShip); } else if (packetID == Packet.ModifyTileList || packetID == Packet.DamageTileGroup || packetID == Packet.DamageTile || packetID == Packet.ConnectWire || packetID == Packet.DisconnectAllWires) { if (!this.client.playerData.canIBuild()) { returnData = false; } } else if (packetID == Packet.EntityCreate) { while (true) { EntityType type = (EntityType)packetData.Read(); if (type == EntityType.EOF) { break; } byte[] entityData = packetData.ReadStarByteArray(); int entityId = packetData.ReadVarInt32(); if (type == EntityType.Projectile) { BinaryReader entity = new BinaryReader(new MemoryStream(entityData)); string projectileKey = entity.ReadStarString(); object projParams = entity.ReadStarVariant(); if (StarryboundServer.config.projectileBlacklist.Contains(projectileKey)) { MemoryStream packet = new MemoryStream(); BinaryWriter packetWrite = new BinaryWriter(packet); packetWrite.WriteVarInt32(entityId); packetWrite.Write(false); this.client.sendClientPacket(Packet.EntityDestroy, packet.ToArray()); returnData = false; } if (StarryboundServer.serverConfig.useDefaultWorldCoordinate && StarryboundServer.config.spawnWorldProtection) { if (this.client.playerData.loc != null) { if (StarryboundServer.config.projectileBlacklistSpawn.Contains(projectileKey) ^ StarryboundServer.config.projectileSpawnListIsWhitelist) { if (StarryboundServer.spawnPlanet.Equals(this.client.playerData.loc) && !this.client.playerData.group.hasPermission("admin.spawnbuild") && !this.client.playerData.inPlayerShip) { MemoryStream packet = new MemoryStream(); BinaryWriter packetWrite = new BinaryWriter(packet); packetWrite.WriteVarInt32(entityId); packetWrite.Write(false); this.client.sendClientPacket(Packet.EntityDestroy, packet.ToArray()); returnData = false; } } } else { MemoryStream packet = new MemoryStream(); BinaryWriter packetWrite = new BinaryWriter(packet); packetWrite.WriteVarInt32(entityId); packetWrite.Write(false); this.client.sendClientPacket(Packet.EntityDestroy, packet.ToArray()); returnData = false; } } } else if (type == EntityType.Object || type == EntityType.Plant || type == EntityType.PlantDrop || type == EntityType.Monster) { if (!this.client.playerData.canIBuild()) { MemoryStream packet = new MemoryStream(); BinaryWriter packetWrite = new BinaryWriter(packet); packetWrite.WriteVarInt32(entityId); packetWrite.Write(false); this.client.sendClientPacket(Packet.EntityDestroy, packet.ToArray()); returnData = false; } } } } else if (packetID == Packet.SpawnEntity) { while (true) { EntityType type = (EntityType)packetData.Read(); if (type == EntityType.EOF) { break; } byte[] entityData = packetData.ReadStarByteArray(); if (type == EntityType.Projectile) { BinaryReader entity = new BinaryReader(new MemoryStream(entityData)); string projectileKey = entity.ReadStarString(); object projParams = entity.ReadStarVariant(); if (StarryboundServer.config.projectileBlacklist.Contains(projectileKey)) { returnData = false; } if (StarryboundServer.serverConfig.useDefaultWorldCoordinate && StarryboundServer.config.spawnWorldProtection) { if (this.client.playerData.loc != null) { if (StarryboundServer.config.projectileBlacklistSpawn.Contains(projectileKey) ^ StarryboundServer.config.projectileSpawnListIsWhitelist) { if (StarryboundServer.spawnPlanet.Equals(this.client.playerData.loc) && !this.client.playerData.group.hasPermission("admin.spawnbuild") && !this.client.playerData.inPlayerShip) { returnData = false; } } } else { returnData = false; } } } else if (type == EntityType.Object || type == EntityType.Plant || type == EntityType.PlantDrop || type == EntityType.Monster) { if (!this.client.playerData.canIBuild()) { returnData = false; } } } } } #endregion else #region Handle Server Packets { if (packetID == Packet.ChatReceive) { returnData = new Packet5ChatReceive(this.client, packetData, this.direction).onReceive(); } else if (packetID == Packet.ProtocolVersion) { uint protocolVersion = packetData.ReadUInt32BE(); if (protocolVersion != StarryboundServer.ProtocolVersion) { MemoryStream packet = new MemoryStream(); BinaryWriter packetWrite = new BinaryWriter(packet); packetWrite.WriteBE(protocolVersion); this.client.sendClientPacket(Packet.ProtocolVersion, packet.ToArray()); this.client.rejectPreConnected("Connection Failed: Unable to handle parent server protocol version."); return; } } else if (packetID == Packet.HandshakeChallenge) { string claimMessage = packetData.ReadString(); string passwordSalt = packetData.ReadStarString(); int passwordRounds = packetData.ReadInt32BE(); MemoryStream packet = new MemoryStream(); BinaryWriter packetWrite = new BinaryWriter(packet); string passwordHash = Utils.StarHashPassword(StarryboundServer.privatePassword, passwordSalt, passwordRounds); packetWrite.WriteStarString(""); packetWrite.WriteStarString(passwordHash); this.client.sendServerPacket(Packet.HandshakeResponse, packet.ToArray()); returnData = false; } else if (packetID == Packet.ConnectResponse) { int startTime = Utils.getTimestamp(); while (true) { if (this.client.state == ClientState.PendingConnectResponse) { break; } if (Utils.getTimestamp() > startTime + StarryboundServer.config.connectTimeout) { this.client.rejectPreConnected("Connection Failed: Client did not respond with handshake."); return; } } returnData = new Packet2ConnectResponse(this.client, packetData, this.direction).onReceive(); } else if (packetID == Packet.WorldStart) { if (!this.client.playerData.sentMotd) { this.client.sendChatMessage(Config.GetMotd()); if (!this.client.playerData.group.hasPermission("world.build")) { this.client.sendChatMessage("^#f75d5d;" + StarryboundServer.config.buildErrorMessage); } this.client.playerData.sentMotd = true; } var unk4 = packetData.ReadStarVariant(); var unk3 = packetData.ReadStarVariant(); byte[] unk1 = packetData.ReadStarByteArray(); byte[] unk2 = packetData.ReadStarByteArray(); float spawnX = packetData.ReadSingleBE(); float spawnY = packetData.ReadSingleBE(); var mapParamsSize = packetData.ReadStarVariant(); /* * for (int i = 0; i < mapParamsSize; i++) * { * string key = packetData.ReadStarString(); * var value = packetData.ReadStarVariant(); * mapParams.Add(key, value); * if(key == "fuel.level") * { * isPlayerShip++; * } * else if(key == "fuel.max") * { * isPlayerShip++; * } * } * this.client.playerData.inPlayerShip = (isPlayerShip == 2); */ uint clientID = packetData.ReadUInt32BE(); bool interpolation = packetData.ReadBoolean(); WorldCoordinate coords = Utils.findGlobalCoords(unk1); if (coords != null) { this.client.playerData.loc = coords; StarryboundServer.logDebug("WorldStart", "[" + this.client.playerData.client + "][" + interpolation + ":" + clientID + "] CurLoc:[" + this.client.playerData.loc.ToString() + "][" + this.client.playerData.inPlayerShip + "]"); } else { StarryboundServer.logDebug("WorldStart", "[" + this.client.playerData.client + "][" + interpolation + ":" + clientID + "] InPlayerShip:[" + this.client.playerData.inPlayerShip + "]"); } } else if (packetID == Packet.WorldStop) { string status = packetData.ReadStarString(); } else if (packetID == Packet.GiveItem) { string name = packetData.ReadStarString(); uint count = packetData.ReadVarUInt32(); var itemDesc = packetData.ReadStarVariant(); } else if (packetID == Packet.EnvironmentUpdate) { byte[] sky = packetData.ReadStarByteArray(); byte[] serverWeather = packetData.ReadStarByteArray(); if (this.client.playerData.loc == null) { WorldCoordinate coords = Utils.findGlobalCoords(sky); if (coords != null) { this.client.playerData.loc = coords; StarryboundServer.logDebug("EnvUpdate", "[" + this.client.playerData.client + "] CurLoc:[" + this.client.playerData.loc.ToString() + "]"); } } } else if (packetID == Packet.ClientContextUpdate) { try { byte[] clientContextData = packetData.ReadStarByteArray(); if (clientContextData.Length != 0) { BinaryReader clientContextReader = new BinaryReader(new MemoryStream(clientContextData)); byte[] data = clientContextReader.ReadStarByteArray(); if (data.Length > 8) //Should at least be more than 8 bytes for it to contain the data we want. { BinaryReader dataReader = new BinaryReader(new MemoryStream(data)); byte dataBufferLength = dataReader.ReadByte(); if (dataBufferLength == 2) { byte arrayLength = dataReader.ReadByte(); if (arrayLength == 2 || arrayLength == 4) //Only observed these being used so far for what we want. { byte dataType = dataReader.ReadByte(); //04 = String, 0E = CelestialLog if (dataType == 4) { string string1 = dataReader.ReadStarString(); if (dataReader.BaseStream.Position != dataReader.BaseStream.Length) { if (string1 == "null") { byte[] worldHeader = dataReader.ReadStarByteArray(); //0008020A000C byte[] worldData = dataReader.ReadStarByteArray(); byte typeByte = dataReader.ReadByte(); //0E = CelestialLog if (typeByte == 14) { Dictionary <string, WorldCoordinate> log = dataReader.ReadStarCelestialLog(); log.TryGetValue("loc", out this.client.playerData.loc); if (!log.TryGetValue("home", out this.client.playerData.home)) { this.client.playerData.home = this.client.playerData.loc; } StarryboundServer.logDebug("ClientContext", "[" + this.client.playerData.client + "] CurLoc:[" + this.client.playerData.loc.ToString() + "][" + this.client.playerData.inPlayerShip + "]"); StarryboundServer.logDebug("ClientContext", "[" + this.client.playerData.client + "] CurHome:[" + this.client.playerData.home.ToString() + "]"); } } } } else if (dataType == 14) { Dictionary <string, WorldCoordinate> log = dataReader.ReadStarCelestialLog(); log.TryGetValue("loc", out this.client.playerData.loc); if (!log.TryGetValue("home", out this.client.playerData.home)) { this.client.playerData.home = this.client.playerData.loc; } StarryboundServer.logDebug("ClientContext", "[" + this.client.playerData.client + "] CurLoc:[" + this.client.playerData.loc.ToString() + "][" + this.client.playerData.inPlayerShip + "]"); StarryboundServer.logDebug("ClientContext", "[" + this.client.playerData.client + "] CurHome:[" + this.client.playerData.home.ToString() + "]"); } } } } } } catch (Exception e) { StarryboundServer.logDebug("ClientContext", "[" + this.client.playerData.client + "] Failed to parse ClientContextUpdate from Server: " + e.ToString()); } } else if (packetID == Packet.EntityCreate) { MemoryStream sendStream = new MemoryStream(); BinaryWriter sendWriter = new BinaryWriter(sendStream); bool test = true; while (true) { EntityType type = (EntityType)packetData.Read(); if (type == EntityType.EOF) { break; } byte[] entityData = packetData.ReadStarByteArray(); int entityId = packetData.ReadVarInt32(); if (type == EntityType.Player) { byte[] buffer = new byte[16]; Buffer.BlockCopy(entityData, 0, buffer, 0, 16); buffer = Utils.HashUUID(buffer); Buffer.BlockCopy(buffer, 0, entityData, 0, 16); returnData = test = false; } sendWriter.Write((byte)type); sendWriter.WriteVarUInt64((ulong)entityData.Length); sendWriter.Write(entityData); sendWriter.WriteVarInt32(entityId); } if (test == false) { this.outgoing.WriteVarUInt32((uint)packetID); this.outgoing.WriteVarInt32((int)sendStream.Length); this.outgoing.Write(sendStream.ToArray()); this.outgoing.Flush(); } } } #endregion } //Check return data if (returnData is Boolean) { if ((Boolean)returnData == false) { continue; } } else if (returnData is int) { if ((int)returnData == -1) { this.client.forceDisconnect(direction, "Command processor requested to drop client"); return; } } #region Forward Packet //Write data to dest this.outgoing.WriteVarUInt32((uint)packetID); if (compressed) { this.outgoing.WriteVarInt32(-packetSize); this.outgoing.Write(dataBuffer, 0, packetSize); } else { this.outgoing.WriteVarInt32(packetSize); this.outgoing.Write(dataBuffer, 0, packetSize); } this.outgoing.Flush(); #endregion #region Inject from Packet Queue var chatbuffer = this.client.packetQueue; foreach (Packet11ChatSend chatPacket in chatbuffer) { chatPacket.onSend(); } this.client.packetQueue = new List <Packet11ChatSend>(); #endregion //If disconnect was forwarded to client, lets disconnect. if (packetID == Packet.ServerDisconnect && direction == Direction.Server) { this.client.closeConnection(); } } } catch (ThreadAbortException) { } catch (EndOfStreamException) { this.client.forceDisconnect(direction, "End of stream"); } catch (Exception e) { if (e.InnerException != null) { if (e.InnerException is System.Net.Sockets.SocketException) { this.client.forceDisconnect(direction, e.InnerException.Message); return; } } this.client.forceDisconnect(direction, "ForwardThread Exception: " + e.ToString()); } }
//----------------------------------------------------------------------------- // Overridden methods //----------------------------------------------------------------------------- protected override void Initialize() { base.Initialize(); string typeName = Properties.GetString("warp_type", "Tunnel"); warpType = (WarpType) Enum.Parse(typeof(WarpType), typeName, true); collisionBox = new Rectangle2I(2, 6, 12, 12); warpEnabled = !IsTouchingPlayer(); // Find the closest room edge. edgeDirection = -1; Rectangle2I roomBounds = RoomControl.RoomBounds; Rectangle2I myBox = new Rectangle2I((int) position.X, (int) position.Y, 16, 16); int minDist = -1; for (int dir = 0; dir < 4; dir++) { int dist = Math.Abs(myBox.GetEdge(dir) - roomBounds.GetEdge(dir)); if (dist < minDist || minDist < 0) { edgeDirection = dir; minDist = dist; } } // Make sure we know if the player respawns on top of this warp point. RoomControl.PlayerRespawn += delegate(Player player) { warpEnabled = !IsTouchingPlayer(); }; // For entrance warp points, intercept room transitions in order to warp. RoomControl.RoomTransitioning += delegate(int direction) { if (warpType == WarpType.Entrance && direction == edgeDirection && IsTouchingPlayer()) { RoomControl.CancelRoomTransition(); Warp(direction); } }; }
//----------------------------------------------------------------------------- // Overridden methods //----------------------------------------------------------------------------- protected override void Initialize() { base.Initialize(); string typeName = Properties.GetString("warp_type", "Tunnel"); warpType = WarpType.Tunnel; if (typeName == "tunnel") warpType = WarpType.Tunnel; else if (typeName == "entrance") warpType = WarpType.Entrance; else if (typeName == "stairs") warpType = WarpType.Stairs; collisionBox = new Rectangle2I(2, 6, 12, 12); warpEnabled = !IsTouchingPlayer(); }
// Methods public AssignWarp(byte[] data) : base(data) { this.unitType = (UnitType) data[1]; this.uid = BitConverter.ToUInt32(data, 2); this.id = (WarpType) data[6]; this.x = BitConverter.ToUInt16(data, 7); this.y = BitConverter.ToUInt16(data, 9); }
public override void NodeGUI() { UnityEditor.EditorGUIUtility.labelWidth = 100; GUILayout.BeginHorizontal (); GUILayout.BeginVertical (); Inputs [0].DisplayLayout (); GUILayout.EndVertical (); GUILayout.BeginVertical (); Outputs [0].DisplayLayout (); GUILayout.EndVertical (); GUILayout.EndHorizontal (); type = (WarpType)UnityEditor.EditorGUILayout.EnumPopup ( new GUIContent ("CSG Operation", "The type of warp performed on Input 1"), type); strength = UnityEditor.EditorGUILayout.Vector3Field ("Strength", strength); if (GUI.changed) NodeEditor.RecalculateFrom (this); }