Browse Source

Change the tree structure so it is compatible with the standard layout
of golang projects.

Beoran 7 years ago
parent
commit
89847b43c2

+ 0 - 0
src/woe/monolog/monolog.go → monolog/monolog.go


+ 152 - 0
raku/raku.go

@@ -0,0 +1,152 @@
+// raku
+
+/* Raku is an easy to use scripting language that can also be used easily interactively
+
+Syntax (verified LL(1) )
+
+PROGRAM -> STATEMENTS .
+STATEMENTS -> STATEMENT STATEMENTS | .
+STATEMENT -> EXPRESSION | BLOCK | EMPTY_LINE | comment .
+EXPRESSION -> VALUE PARAMETERS NL.
+PARAMETERS_NONEMPTY -> PARAMETER PARAMETERS.
+PARAMETERS-> PARAMETERS_NONEMPTY | .
+PARAMETER -> BLOCK | VALUE .
+EMPTY_LINE -> NL .
+BLOCK -> ob STATEMENTS cb | op STATEMENTS cp | oa STATEMENTS ca.
+NL -> nl | semicolon .
+VALUE -> string | float | integer | symbol .
+
+Lexer:
+
+
+*/
+package raku
+
+import (
+	"fmt"
+	"io"
+)
+
+type Value string
+type TokenType int
+
+type Position struct {
+	Index  int
+	Row    int
+	Column int
+}
+
+const (
+	TokenError TokenType = iota
+	TokenEOF
+)
+
+type Token struct {
+	TokenType
+	Value
+	Position
+}
+
+func (me Token) String() string {
+	return fmt.Sprintf("Token: %d >%s< %d %d %d.", me.TokenType, string(me.Value), me.Index, me.Row, me.Column)
+}
+
+type TokenChannel chan Token
+
+type Lexer struct {
+	Reader  io.Reader
+	Current Position
+	Last    Position
+	Token   Token
+	rule    LexerRule
+	Output  TokenChannel
+	buffer  []byte
+}
+
+type LexerRule func(lexer *Lexer) LexerRule
+
+func (lexer *Lexer) Emit(t TokenType, v Value) {
+	tok := Token{t, v, lexer.Current}
+	lexer.Output <- tok
+}
+
+func (lexer *Lexer) Error(message string, args ...interface{}) {
+	value := fmt.Sprintf(message, args...)
+	lexer.Emit(TokenError, Value(value))
+}
+
+func LexError(lexer *Lexer) LexerRule {
+	lexer.Error("Error")
+	return nil
+}
+
+func LexNormal(lexer *Lexer) LexerRule {
+	return LexError
+}
+
+func OpenLexer(reader io.Reader) *Lexer {
+	lexer := &Lexer{}
+	lexer.Reader = reader
+	lexer.Output = make(TokenChannel)
+	// lexer.buffer = new(byte[1024])
+	return lexer
+}
+
+func (me *Lexer) ReadReader() (bool, error) {
+	buffer := make([]byte, 1024)
+
+	n, err := me.Reader.Read(buffer)
+	if n > 0 {
+		me.buffer = append(me.buffer, buffer...)
+	}
+	if err == io.EOF {
+		me.Emit(TokenEOF, "")
+		return true, nil
+	} else if err != nil {
+		me.Error("Error reading from reader: %s", err)
+		return true, err
+	}
+	return false, nil
+}
+
+func (me *Lexer) Start() {
+	more, err := me.ReadReader()
+	for err == nil && more {
+		more, err = me.ReadReader()
+	}
+
+	if err != nil {
+		return
+	}
+
+	rule := LexNormal
+	for rule != nil {
+		rule = rule(me)
+	}
+
+	close(me.Output)
+}
+
+/*
+func (me *Lexer) TryLexing() {
+	go {
+		me.Start()
+	}
+
+	for token := range me.Output {
+		fmt.Println("Token %s", token)
+	}
+}
+*/
+
+type Parser struct {
+	Lexer
+}
+
+type Environment struct {
+	Parent *Environment
+}
+
+func main() {
+	fmt.Println("Hello World!")
+}

+ 0 - 0
src/woe/server/action.go → server/action.go


+ 516 - 0
server/ask.go

@@ -0,0 +1,516 @@
+package server
+
+/* This file contains dialog helpers for the client. */
+
+// import "github.com/beoran/woe/monolog"
+import t "github.com/beoran/woe/telnet"
+import "github.com/beoran/woe/telnet"
+import "github.com/beoran/woe/world"
+
+// import "github.com/beoran/woe/monolog"
+import "bytes"
+import "strings"
+import "regexp"
+
+// import "fmt"
+import "strconv"
+
+// import "strings"
+
+// Switches to "password" mode.
+func (me *Client) PasswordMode() telnet.Event {
+	// The server sends "IAC WILL ECHO", meaning "I, the server, will do any
+	// echoing from now on." The client should acknowledge this with an IAC DO
+	// ECHO, and then stop putting echoed text in the input buffer.
+	// It should also do whatever is appropriate for password entry to the input
+	// box thing - for example, it might * it out. Text entered in server-echoes
+	// mode should also not be placed any command history.
+	// don't use the Q state machne for echos
+	me.telnet.TelnetSendBytes(t.TELNET_IAC, t.TELNET_WILL, t.TELNET_TELOPT_ECHO)
+	tev, _, _ := me.TryReadEvent(100)
+	if tev != nil && !telnet.IsEventType(tev, t.TELNET_DO_EVENT) {
+		return tev
+	}
+	return nil
+}
+
+// Switches to "normal, or non-password mode.
+func (me *Client) NormalMode() telnet.Event {
+	// When the server wants the client to start local echoing again, it s}s
+	// "IAC WONT ECHO" - the client must respond to this with "IAC DONT ECHO".
+	// Again don't use Q state machine.
+	me.telnet.TelnetSendBytes(t.TELNET_IAC, t.TELNET_WONT, t.TELNET_TELOPT_ECHO)
+	tev, _, _ := me.TryReadEvent(100)
+	if tev != nil && !telnet.IsEventType(tev, t.TELNET_DONT_EVENT) {
+		return tev
+	}
+	return nil
+}
+
+func (me *Client) Printf(format string, args ...interface{}) {
+	me.telnet.TelnetPrintf(format, args...)
+}
+
+func (me *Client) ColorTest() {
+	me.Printf("\033[1mBold\033[0m\r\n")
+	me.Printf("\033[3mItalic\033[0m\r\n")
+	me.Printf("\033[4mUnderline\033[0m\r\n")
+	for fg := 30; fg < 38; fg++ {
+		me.Printf("\033[%dmForeground Color %d\033[0m\r\n", fg, fg)
+		me.Printf("\033[1;%dmBold Foreground Color %d\033[0m\r\n", fg, fg)
+	}
+
+	for bg := 40; bg < 48; bg++ {
+		me.Printf("\033[%dmBackground Color %d\033[0m\r\n", bg, bg)
+		me.Printf("\033[1;%dmBold Background Color %d\033[0m\r\n", bg, bg)
+	}
+}
+
+// Blockingly reads a single command from the client
+func (me *Client) ReadCommand() (something []byte) {
+	something = nil
+	for something == nil {
+		something, _, _ = me.TryRead(-1)
+		if something != nil {
+			something = bytes.TrimRight(something, "\r\n")
+			return something
+		}
+	}
+	return nil
+}
+
+func (me *Client) AskSomething(prompt string, re string, nomatch_prompt string, noecho bool) (something []byte) {
+	something = nil
+
+	if noecho {
+		me.PasswordMode()
+	}
+
+	for something == nil || len(something) == 0 {
+		me.Printf("%s", prompt)
+		something, _, _ = me.TryRead(-1)
+		if something != nil {
+			something = bytes.TrimRight(something, "\r\n")
+			if len(re) > 0 {
+				ok, _ := regexp.Match(re, something)
+				if !ok {
+					me.Printf("\n%s\n", nomatch_prompt)
+					something = nil
+				}
+			}
+		}
+	}
+
+	if noecho {
+		me.NormalMode()
+		me.Printf("\n")
+	}
+
+	return something
+}
+
+func (me *Client) AskYesNo(prompt string) bool {
+	res := me.AskSomething(prompt+" (y/n)", "[ynYN]", "Please answer y or n.", false)
+	if res[0] == 'Y' || res[0] == 'y' {
+		return true
+	} else {
+		return false
+	}
+}
+
+// Interface for an item in a list of options.
+type AskOption interface {
+	// Name of the option, also used to compare input
+	AskName() string
+	// Short description of the option, shown after name
+	AskShort() string
+	// Long description, displayed if "help name" is requested
+	AskLong() string
+	// Accound privilege required or the option to be selectable.
+	AskPrivilege() world.Privilege
+}
+
+type AskOptionList interface {
+	AskOptionListLen() int
+	AskOptionListGet(index int) AskOption
+}
+
+type TrivialAskOption string
+
+func (me TrivialAskOption) AskName() string {
+	return string(me)
+}
+
+func (me TrivialAskOption) AskLong() string {
+	return ""
+}
+
+func (me TrivialAskOption) AskShort() string {
+	return ""
+}
+
+func (me TrivialAskOption) AskPrivilege() world.Privilege {
+	return world.PRIVILEGE_ZERO
+}
+
+type TrivialAskOptionList []TrivialAskOption
+
+func (me TrivialAskOptionList) AskOptionListLen() int {
+	return len(me)
+}
+
+func (me TrivialAskOptionList) AskOptionListGet(index int) AskOption {
+	return me[index]
+}
+
+type SimpleAskOption struct {
+	Name      string
+	Short     string
+	Long      string
+	Privilege world.Privilege
+}
+
+func (me SimpleAskOption) AskName() string {
+	return me.Name
+}
+
+func (me SimpleAskOption) AskShort() string {
+	return me.Short
+}
+
+func (me SimpleAskOption) AskLong() string {
+	return me.Long
+}
+
+func (me SimpleAskOption) AskPrivilege() world.Privilege {
+	return me.Privilege
+}
+
+type SimpleAskOptionList []SimpleAskOption
+
+func (me SimpleAskOptionList) AskOptionListLen() int {
+	return len(me)
+}
+
+func (me SimpleAskOptionList) AskOptionListGet(index int) AskOption {
+	return me[index]
+}
+
+type JobListAsker []world.Job
+
+func (me JobListAsker) AskOptionListLen() int {
+	return len(me)
+}
+
+func (me JobListAsker) AskOptionListGet(index int) AskOption {
+	return me[index]
+}
+
+type KinListAsker []world.Kin
+
+func (me KinListAsker) AskOptionListLen() int {
+	return len(me)
+}
+
+func (me KinListAsker) AskOptionListGet(index int) AskOption {
+	return me[index]
+}
+
+type GenderListAsker []world.Gender
+
+func (me GenderListAsker) AskOptionListLen() int {
+	return len(me)
+}
+
+func (me GenderListAsker) AskOptionListGet(index int) AskOption {
+	return me[index]
+}
+
+type AskOptionSlice []AskOption
+type AskOptionFilterFunc func(AskOption) AskOption
+
+func (me AskOptionSlice) AskOptionListLen() int {
+	return len(me)
+}
+
+func (me AskOptionSlice) AskOptionListGet(index int) AskOption {
+	return me[index]
+}
+
+func AskOptionListEach(me AskOptionList, cb AskOptionFilterFunc) AskOption {
+	for i := 0; i < me.AskOptionListLen(); i++ {
+		res := cb(me.AskOptionListGet(i))
+		if res != nil {
+			return res
+		}
+	}
+	return nil
+}
+
+func AskOptionListFilter(me AskOptionList, cb AskOptionFilterFunc) AskOptionList {
+	result := make(AskOptionSlice, 0)
+	for i := 0; i < me.AskOptionListLen(); i++ {
+		res := cb(me.AskOptionListGet(i))
+		if res != nil {
+			result = append(result, res)
+		}
+	}
+	return result
+}
+
+/* Finds the name irrespecful of the case */
+func AskOptionListFindName(me AskOptionList, name string) AskOption {
+	return AskOptionListEach(me, func(e AskOption) AskOption {
+		if strings.ToLower(e.AskName()) == strings.ToLower(name) {
+			return e
+		} else {
+			return nil
+		}
+	})
+}
+
+/* Filters the list by privilege level (only those allowed by the level are retained) */
+func AskOptionListFilterPrivilege(me AskOptionList, privilege world.Privilege) AskOptionList {
+	return AskOptionListFilter(me, func(e AskOption) AskOption {
+		if e.AskPrivilege() <= privilege {
+			return e
+		} else {
+			return nil
+		}
+	})
+}
+
+type MergedAskOptionList struct {
+	head AskOptionList
+	tail AskOptionList
+}
+
+func (me *MergedAskOptionList) AskOptionListLen() int {
+	return me.head.AskOptionListLen() + me.tail.AskOptionListLen()
+}
+
+func (me *MergedAskOptionList) AskOptionListGet(index int) AskOption {
+	headlen := me.head.AskOptionListLen()
+	if index < headlen {
+		return me.head.AskOptionListGet(index)
+	}
+	return me.tail.AskOptionListGet(index - headlen)
+}
+
+/* Merges two AskOptionLists without copying using the MergedAskOptionList rtype  */
+func AskOptionListMerge(me AskOptionList, you AskOptionList) AskOptionList {
+	return &MergedAskOptionList{me, you}
+}
+
+func (me AskOptionSlice) Each(cb AskOptionFilterFunc) AskOption {
+	for i := 0; i < len(me); i++ {
+		res := cb(me[i])
+		if res != nil {
+			return res
+		}
+	}
+	return nil
+}
+
+func (me AskOptionSlice) Filter(cb AskOptionFilterFunc) AskOptionSlice {
+	result := make(AskOptionSlice, 0)
+	for i := 0; i < len(me); i++ {
+		res := cb(me[i])
+		if res != nil {
+			result = append(result, res)
+		}
+	}
+	return result
+}
+
+/* Finds the name irrespecful of the case */
+func (me AskOptionSlice) FindName(name string) AskOption {
+	return me.Each(func(e AskOption) AskOption {
+		if strings.ToLower(e.AskName()) == strings.ToLower(name) {
+			return e
+		} else {
+			return nil
+		}
+	})
+}
+
+/* Filters the list by privilege level (only those allowed by the level are retained) */
+func (me AskOptionSlice) FilterPrivilege(privilege world.Privilege) AskOptionSlice {
+	return me.Filter(func(e AskOption) AskOption {
+		if e.AskPrivilege() <= privilege {
+			return e
+		} else {
+			return nil
+		}
+	})
+}
+
+func (me *Client) AskOptionListHelp(alist AskOptionList, input []byte) {
+	re := regexp.MustCompile("[^ \t,]+")
+	argv := re.FindAll(input, -1)
+	if len(argv) < 2 {
+		me.Printf("Help usage: help <topic>.\n")
+		return
+	}
+	e := AskOptionListFindName(alist, string(argv[1]))
+	if e == nil {
+		me.Printf("Cannot find topic %s in list. No help available.\n", string(argv[1]))
+	} else {
+		al := e.AskLong()
+		if al == "" {
+			me.Printf("Topic %s found, but help is unavailable.\n", string(argv[1]))
+		} else {
+			me.Printf("Help on %s:\n%s\n", string(argv[1]), e.AskLong())
+		}
+	}
+}
+
+func (me *Client) AskOptionListOnce(heading string, prompt string, noecho bool, alist AskOptionList) (result AskOption) {
+	list := AskOptionListFilterPrivilege(alist, me.account.Privilege)
+	me.Printf("\n%s\n\n", heading)
+	for i := 0; i < list.AskOptionListLen(); i++ {
+		v := list.AskOptionListGet(i)
+		sh := v.AskShort()
+		if sh == "" {
+			me.Printf("[%d] %s\n", i+1, v.AskName())
+		} else {
+			me.Printf("[%d] %s: %s\n", i+1, v.AskName(), sh)
+		}
+	}
+	me.Printf("\n")
+	aid := me.AskSomething(prompt, "", "", false)
+	iresp, err := strconv.Atoi(string(aid))
+	if err != nil { /* Try by name if not a number. */
+		e := AskOptionListFindName(alist, string(aid))
+		if e != nil {
+			return e
+		} else if ok, _ := regexp.Match("help", bytes.ToLower(aid)); ok {
+			me.AskOptionListHelp(list, aid)
+		} else {
+			me.Printf("Name not found in list. Please choose a number or name from the list above. Or type help <option> for help on that option.\n")
+		}
+	} else if (iresp > 0) && (iresp <= list.AskOptionListLen()) {
+		/* In range of list. */
+		return list.AskOptionListGet(iresp - 1)
+	} else {
+		me.Printf("Please choose a number or name from the list above.\n")
+	}
+	return nil
+}
+
+func (me *Client) AskOptionList(
+	heading string, prompt string, noecho bool,
+	noconfirm bool, list AskOptionList) (result AskOption) {
+	for {
+		result = me.AskOptionListOnce(heading, prompt, noecho, list)
+		if result != nil {
+			if noconfirm || me.AskYesNo(heading+"\nConfirm "+result.AskName()+"? ") {
+				return result
+			}
+		}
+	}
+}
+
+func (me *Client) AskOptionListExtra(heading string,
+	prompt string, noecho bool, noconfirm bool, list AskOptionList,
+	extra AskOptionList) (result AskOption) {
+	xlist := AskOptionListMerge(list, extra)
+	return me.AskOptionList(heading, prompt, noecho, noconfirm, xlist)
+}
+
+func (me *Client) AskEntityListOnce(
+	heading string, prompt string, noecho bool,
+	elist world.EntitylikeSlice, extras []string) (result world.Entitylike, alternative string) {
+	list := elist.FilterPrivilege(me.account.Privilege)
+	me.Printf("\n%s\n\n", heading)
+	last := 0
+	for i, v := range list {
+		e := v.AsEntity()
+		me.Printf("[%d] %s: %s\n", i+1, e.Name, e.Short)
+		last = i + 1
+	}
+
+	if extras != nil {
+		for i, v := range extras {
+			me.Printf("[%d] %s\n", last+i+1, v)
+		}
+	}
+
+	me.Printf("\n")
+	aid := me.AskSomething(prompt, "", "", false)
+	iresp, err := strconv.Atoi(string(aid))
+	if err != nil { /* Try by name if not a number. */
+		e := list.FindName(string(aid))
+		if e != nil {
+			return e, ""
+		} else {
+			if extras != nil {
+				for _, v := range extras {
+					if strings.ToLower(v) == strings.ToLower(string(aid)) {
+						return nil, v
+					}
+				}
+			}
+			me.Printf("Name not found in list. Please choose a number or name from the list above.\n")
+		}
+	} else if (iresp > 0) && (iresp <= len(list)) { /* In range of list. */
+		return list[iresp-1], ""
+	} else if (extras != nil) && (iresp > last) && (iresp <= last+len(extras)) {
+		return nil, extras[iresp-last-1]
+	} else {
+		me.Printf("Please choose a number or name from the list above.\n")
+	}
+	return nil, ""
+}
+
+func (me *Client) AskEntityList(
+	heading string, prompt string, noecho bool,
+	noconfirm bool, list world.EntitylikeSlice, extras []string) (result world.Entitylike, alternative string) {
+	for {
+		result, alternative = me.AskEntityListOnce(heading, prompt, noecho, list, extras)
+		if result != nil {
+			e := result.AsEntity()
+			if !noconfirm {
+				me.Printf("\n%s: %s\n\n%s\n\n", e.Name, e.Short, e.Long)
+			}
+			if noconfirm || me.AskYesNo(heading+"\nConfirm "+e.Name+"? ") {
+				return result, ""
+			}
+		} else if alternative != "" {
+			if noconfirm || me.AskYesNo("Confirm "+alternative+" ?") {
+				return result, alternative
+			}
+		}
+	}
+}
+
+const LOGIN_RE = "^[A-Za-z][A-Za-z0-9]*$"
+
+func (me *Client) AskLogin() []byte {
+	return me.AskSomething("Login?>", LOGIN_RE, "Login must consist of letters followed by letters or numbers.", false)
+}
+
+const EMAIL_RE = "@"
+
+func (me *Client) AskEmail() []byte {
+	return me.AskSomething("E-mail?>", EMAIL_RE, "Email must have at least an @ in there somewhere.", false)
+}
+
+const CHARNAME_RE = "^[A-Z][A-Za-z]+$"
+
+func (me *Client) AskCharacterName() []byte {
+	return me.AskSomething("Character Name?>", CHARNAME_RE, "Character name consist of a capital letter followed by at least one letter.", false)
+}
+
+func (me *Client) AskPassword() []byte {
+	return me.AskSomething("Password?>", "", "", true)
+}
+
+func (me *Client) AskRepeatPassword() []byte {
+	return me.AskSomething("Repeat Password?>", "", "", true)
+}
+
+func (me *Client) HandleCommand() {
+	command := me.ReadCommand()
+	me.ProcessCommand(command)
+}

+ 236 - 0
server/client.go

@@ -0,0 +1,236 @@
+package server
+
+import (
+	// "fmt"
+	"net"
+	"time"
+	// "errors"
+	// "io"
+	"github.com/beoran/woe/monolog"
+	"github.com/beoran/woe/telnet"
+	"github.com/beoran/woe/world"
+)
+
+/* Specific properties of a client. */
+type ClientInfo struct {
+	w         int
+	h         int
+	mtts      int
+	naws      bool
+	compress2 bool
+	mssp      bool
+	zmp       bool
+	msp       bool
+	msdp      bool
+	mxp       bool
+	ttype     bool
+	terminals []string
+	terminal  string
+}
+
+type Client struct {
+	server   *Server
+	id       int
+	conn     net.Conn
+	alive    bool
+	timeout  int
+	datachan chan []byte
+	errchan  chan error
+	timechan chan time.Time
+	telnet   *telnet.Telnet
+	info     ClientInfo
+
+	// Account of client or nil if not yet selected.
+	account *world.Account
+	// Character client is plaing with or nil if not yet selected.
+	character *world.Character
+	// Message channels that this client is listening to once fully logged in.
+	// Not to be confused with Go channels.
+	channels map[string]bool
+}
+
+func NewClient(server *Server, id int, conn net.Conn) *Client {
+	datachan := make(chan []byte, 1024)
+	errchan := make(chan error, 1)
+	timechan := make(chan time.Time, 32)
+	telnet := telnet.New()
+	channels := make(map[string]bool)
+	info := ClientInfo{w: -1, h: -1, terminal: "none"}
+	return &Client{server, id, conn, true, -1, datachan, errchan, timechan, telnet, info, nil, nil, channels}
+}
+
+func (me *Client) Close() {
+	me.conn.Close()
+	me.alive = false
+	if me.account != nil {
+		me.server.World.RemoveAccount(me.account.Name)
+	}
+	me.account = nil
+}
+
+/** Goroutine that does the actual reading of input data, and sends it to the
+ * needed channels. */
+func (me *Client) ServeRead() {
+	for me.alive {
+		buffer := make([]byte, 1024, 1024)
+		read, err := me.conn.Read(buffer)
+		if err != nil {
+			me.errchan <- err
+			return
+		}
+		// reply will be stored in me.telnet.Events channel
+		me.telnet.ProcessBytes(buffer[:read])
+	}
+}
+
+/* Goroutine that sends any data that must be sent through the Telnet protocol
+ * to the connected client.
+ */
+func (me *Client) ServeWrite() {
+	for me.alive {
+		select {
+		case data := <-me.telnet.ToClient:
+			monolog.Log("SERVEWRITE", "Will send to client: %v", data)
+			me.conn.Write(data)
+		}
+	}
+}
+
+func (me *Client) TryReadEvent(millis int) (event telnet.Event, timeout bool, close bool) {
+	var timerchan <-chan (time.Time)
+
+	if millis >= 0 {
+		timerchan = time.Tick(time.Millisecond * time.Duration(millis))
+	} else {
+		/* If time is negative, block by using a fake time channel that never gets sent anyting */
+		timerchan = make(<-chan (time.Time))
+	}
+
+	select {
+	case event := <-me.telnet.Events:
+		return event, false, false
+
+	case err := <-me.errchan:
+		monolog.Info("Connection closed: %s\n", err)
+		me.Close()
+		return nil, false, true
+
+	case _ = <-timerchan:
+		return nil, true, false
+	}
+}
+
+func (me *Client) HandleNAWSEvent(nawsevent *telnet.NAWSEvent) {
+	me.info.w = nawsevent.W
+	me.info.h = nawsevent.H
+	monolog.Info("Client %d window size #{%d}x#{%d}", me.id, me.info.w, me.info.h)
+	me.info.naws = true
+}
+
+func (me *Client) TryRead(millis int) (data []byte, timeout bool, close bool) {
+
+	for me.alive {
+		event, timeout, close := me.TryReadEvent(millis)
+		if event == nil && (timeout || close) {
+			return nil, timeout, close
+		}
+		switch event := event.(type) {
+		case *telnet.DataEvent:
+			monolog.Log("TELNETDATAEVENT", "Telnet data event %T : %d.", event, len(event.Data))
+			return event.Data, false, false
+		case *telnet.NAWSEvent:
+			monolog.Log("TELNETNAWSEVENT", "Telnet NAWS event %T.", event)
+			me.HandleNAWSEvent(event)
+		default:
+			monolog.Info("Ignoring telnet event %T : %v for now.", event, event)
+		}
+	}
+
+	return nil, false, true
+}
+
+func (me *Client) Serve() (err error) {
+	// buffer := make([]byte, 1024, 1024)
+	go me.ServeWrite()
+	go me.ServeRead()
+	me.SetupTelnet()
+	if me.server.World != nil {
+		me.Printf(me.server.World.MOTD)
+	}
+	if !me.AccountDialog() {
+		time.Sleep(3)
+		// sleep so output gets flushed, hopefully.
+		// Also slow down brute force attacks.
+		me.Close()
+		return nil
+	}
+
+	if !me.CharacterDialog() {
+		time.Sleep(3)
+		// sleep so output gets flushed, hopefully.
+		// Also slow down brute force attacks.
+		me.Close()
+		return nil
+	}
+
+	me.Printf("Welcome, %s\n", me.account.Name)
+
+	for me.alive {
+		me.HandleCommand()
+		/*
+		   data, _, _ := me.TryRead(3000)
+
+		   if data == nil {
+		      // me.telnet.TelnetPrintf("Too late!\r\n")
+		   } else {
+		       me.server.Broadcast(string(data))
+		   }
+		*/
+
+	}
+	return nil
+}
+
+func (me *Client) Disconnect() {
+	me.alive = false
+}
+
+func (me *Client) IsAlive() bool {
+	return me.alive
+}
+
+func (me *Client) IsLoginFinished() bool {
+	return me.IsAlive() && (me.account != nil) && (me.character != nil)
+}
+
+func (me *Client) SetChannel(channelname string, value bool) {
+	me.channels[channelname] = value
+}
+
+func (me *Client) IsListeningToChannel(channelname string) bool {
+	res, ok := me.channels[channelname]
+	// All chanels are active by default, and must be actively disabled by the client.
+	if ok && (!res) {
+		return false
+	}
+	return true
+}
+
+func (me *Client) WriteString(str string) {
+	me.conn.Write([]byte(str))
+}
+
+/** Accessor */
+func (me *Client) GetServer() *Server {
+	return me.server
+}
+
+/** Accessor */
+func (me *Client) GetWorld() *world.World {
+	return me.server.World
+}
+
+/** Accessor */
+func (me *Client) GetAccount() *world.Account {
+	return me.account
+}

+ 279 - 0
server/dialog.go

@@ -0,0 +1,279 @@
+// dialog.go
+package server
+
+/* This file contains dialogs for the client. The dialog helpers are in ask.go. */
+
+import "github.com/beoran/woe/monolog"
+import "github.com/beoran/woe/world"
+
+const NEW_CHARACTER_PRICE = 4
+
+func (me *Client) ExistingAccountDialog() bool {
+	pass := me.AskPassword()
+	for pass == nil {
+		me.Printf("Password may not be empty!\n")
+		pass = me.AskPassword()
+	}
+
+	if !me.account.Challenge(string(pass)) {
+		me.Printf("Password not correct!\n")
+		me.Printf("Disconnecting!\n")
+		return false
+	}
+	return true
+}
+
+func (me *Client) NewAccountDialog(login string) bool {
+	for me.account == nil {
+		me.Printf("\nWelcome, %s! Creating new account...\n", login)
+		pass1 := me.AskPassword()
+
+		if pass1 == nil {
+			return false
+		}
+
+		pass2 := me.AskRepeatPassword()
+
+		if pass1 == nil {
+			return false
+		}
+
+		if string(pass1) != string(pass2) {
+			me.Printf("\nPasswords do not match! Please try again!\n")
+			continue
+		}
+
+		email := me.AskEmail()
+		if email == nil {
+			return false
+		}
+
+		me.account = world.NewAccount(login, string(pass1), string(email), 7)
+		err := me.account.Save(me.server.DataPath())
+
+		if err != nil {
+			monolog.Error("Could not save account %s: %v", login, err)
+			me.Printf("\nFailed to save your account!\nPlease contact a WOE administrator!\n")
+			return false
+		}
+
+		monolog.Info("Created new account %s", login)
+		me.Printf("\nSaved your account.\n")
+		return true
+	}
+	return false
+}
+
+func (me *Client) AccountDialog() bool {
+	login := me.AskLogin()
+	if login == nil {
+		return false
+	}
+	var err error
+
+	if me.server.World.GetAccount(string(login)) != nil {
+		me.Printf("Account already logged in!\n")
+		me.Printf("Disconnecting!\n")
+		return false
+	}
+
+	me.account, err = me.server.World.LoadAccount(string(login))
+	if err != nil {
+		monolog.Warning("Could not load account %s: %v", login, err)
+	}
+	if me.account != nil {
+		return me.ExistingAccountDialog()
+	} else {
+		return me.NewAccountDialog(string(login))
+	}
+}
+
+func (me *Client) NewCharacterDialog() bool {
+	noconfirm := true
+	extra := TrivialAskOptionList{TrivialAskOption("Cancel")}
+
+	me.Printf("New character:\n")
+	charname := me.AskCharacterName()
+
+	existing, aname, _ := world.LoadCharacterByName(me.server.DataPath(), string(charname))
+
+	for existing != nil {
+		if aname == me.account.Name {
+			me.Printf("You already have a character with a similar name!\n")
+		} else {
+			me.Printf("That character name is already taken by someone else.\n")
+		}
+		charname := me.AskCharacterName()
+		existing, aname, _ = world.LoadCharacterByName(me.server.DataPath(), string(charname))
+	}
+
+	kinres := me.AskOptionListExtra("Please choose the kin of this character", "Kin?> ", false, noconfirm, KinListAsker(world.KinList), extra)
+
+	if sopt, ok := kinres.(TrivialAskOption); ok {
+		if string(sopt) == "Cancel" {
+			me.Printf("Character creation canceled.\n")
+			return true
+		} else {
+			return true
+		}
+	}
+
+	kin := kinres.(world.Kin)
+
+	genres := me.AskOptionListExtra("Please choose the gender of this character", "Gender?> ", false, noconfirm, GenderListAsker(world.GenderList), extra)
+	if sopt, ok := kinres.(TrivialAskOption); ok {
+		if string(sopt) == "Cancel" {
+			me.Printf("Character creation canceled.\n")
+			return true
+		} else {
+			return true
+		}
+	}
+
+	gender := genres.(world.Gender)
+
+	jobres := me.AskOptionListExtra("Please choose the job of this character", "Gender?> ", false, noconfirm, JobListAsker(world.JobList), extra)
+	if sopt, ok := kinres.(TrivialAskOption); ok {
+		if string(sopt) == "Cancel" {
+			me.Printf("Character creation canceled.\n")
+			return true
+		} else {
+			return true
+		}
+	}
+
+	job := jobres.(world.Job)
+
+	character := world.NewCharacter(me.account,
+		string(charname), &kin, &gender, &job)
+
+	me.Printf("%s", character.Being.ToStatus())
+
+	ok := me.AskYesNo("Is this character ok?")
+
+	if !ok {
+		me.Printf("Character creation canceled.\n")
+		return true
+	}
+
+	me.account.AddCharacter(character)
+	me.account.Points -= NEW_CHARACTER_PRICE
+	me.account.Save(me.server.DataPath())
+	character.Save(me.server.DataPath())
+	me.Printf("Character %s saved.\n", character.Being.Name)
+
+	return true
+}
+
+func (me *Client) DeleteCharacterDialog() bool {
+	extra := []AskOption{TrivialAskOption("Cancel"), TrivialAskOption("Disconnect")}
+
+	els := me.AccountCharacterList()
+	els = append(els, extra...)
+	result := me.AskOptionList("Character to delete?",
+		"Character?>", false, false, els)
+
+	if alt, ok := result.(TrivialAskOption); ok {
+		if string(alt) == "Disconnect" {
+			me.Printf("Disconnecting")
+			return false
+		} else if string(alt) == "Cancel" {
+			me.Printf("Canceled")
+			return true
+		} else {
+			monolog.Warning("Internal error, unhandled case.")
+			return true
+		}
+	}
+
+	character := result.(*world.Character)
+	/* A character that is deleted gives NEW_CHARACTER_PRICE +
+	 * level / (NEW_CHARACTER_PRICE * 2) points, but only after the delete. */
+	np := NEW_CHARACTER_PRICE + character.Level/(NEW_CHARACTER_PRICE*2)
+	me.account.DeleteCharacter(me.server.DataPath(), character)
+	me.account.Points += np
+
+	return true
+}
+
+func (me *Client) AccountCharacterList() AskOptionSlice {
+	els := make(AskOptionSlice, 0, 16)
+	for i := 0; i < me.account.NumCharacters(); i++ {
+		chara := me.account.GetCharacter(i)
+		els = append(els, chara)
+	}
+	return els
+}
+
+func (me *Client) ChooseCharacterDialog() bool {
+	extra := []AskOption{
+		SimpleAskOption{"New", "Create New character",
+			"Create a new character. This option costs 4 points.",
+			world.PRIVILEGE_ZERO},
+		SimpleAskOption{"Disconnect", "Disconnect from server",
+			"Disconnect your client from this server.",
+			world.PRIVILEGE_ZERO},
+		SimpleAskOption{"Delete", "Delete character",
+			"Delete a character. A character that has been deleted cannot be reinstated. You will receive point bonuses for deleting your characters that depend on their level.",
+			world.PRIVILEGE_ZERO},
+	}
+
+	var pchara *world.Character = nil
+
+	for pchara == nil {
+		els := me.AccountCharacterList()
+		els = append(els, extra...)
+		result := me.AskOptionList("Choose a character?", "Character?>", false, true, els)
+		switch opt := result.(type) {
+		case SimpleAskOption:
+			if opt.Name == "New" {
+				if me.account.Points >= NEW_CHARACTER_PRICE {
+					if !me.NewCharacterDialog() {
+						return false
+					}
+				} else {
+					me.Printf("Sorry, you have no points left to make new characters!\n")
+				}
+			} else if opt.Name == "Disconnect" {
+				me.Printf("Disconnecting\n")
+				return false
+			} else if opt.Name == "Delete" {
+				if !me.DeleteCharacterDialog() {
+					return false
+				}
+			} else {
+				me.Printf("Internal error, alt not valid: %v.", opt)
+			}
+		case *world.Character:
+			pchara = opt
+		default:
+			me.Printf("What???")
+		}
+		me.Printf("You have %d points left.\n", me.account.Points)
+	}
+
+	me.character = pchara
+	me.Printf("%s\n", me.character.Being.ToStatus())
+	me.Printf("Welcome, %s!\n", me.character.Name)
+
+	return true
+}
+
+func (me *Client) CharacterDialog() bool {
+	me.Printf("You have %d remaining points.\n", me.account.Points)
+	for me.account.NumCharacters() < 1 {
+		me.Printf("You have no characters yet!\n")
+		if me.account.Points >= NEW_CHARACTER_PRICE {
+			if !me.NewCharacterDialog() {
+				return false
+			}
+		} else {
+			me.Printf("Sorry, you have no characters, and no points left to make new characters!\n")
+			me.Printf("Please contact the staff of WOE if you think this is a mistake.\n")
+			me.Printf("Disconnecting!\n")
+			return false
+		}
+	}
+
+	return me.ChooseCharacterDialog()
+}

+ 345 - 0
server/server.go

@@ -0,0 +1,345 @@
+package server
+
+import (
+	//  "io"
+	"net"
+	//  "errors"
+	"fmt"
+	"math/rand"
+	"os"
+	"path/filepath"
+	"time"
+
+	"github.com/beoran/woe/monolog"
+	"github.com/beoran/woe/world"
+)
+
+var MSSP map[string]string
+
+const STATUS_OK = 0
+const STATUS_CANNOT_LISTEN = 1
+const STATUS_RESTART = 0
+const STATUS_SHUTDOWN = 4
+const MAX_CLIENTS = 1000
+
+func init() {
+	MSSP = map[string]string{
+		"NAME":             "Workers Of Eruta",
+		"UPTIME":           string(time.Now().Unix()),
+		"PLAYERS":          "0",
+		"CRAWL DELAY":      "0",
+		"CODEBASE":         "WOE",
+		"CONTACT":          "beoran@gmail.com",
+		"CREATED":          "2015",
+		"ICON":             "None",
+		"LANGUAGE":         "English",
+		"LOCATION":         "USA",
+		"MINIMUM AGE":      "18",
+		"WEBSITE":          "beoran.net",
+		"FAMILY":           "Custom",
+		"GENRE":            "Science Fiction",
+		"GAMEPLAY":         "Adventure",
+		"STATUS":           "Alpha",
+		"GAMESYSTEM":       "Custom",
+		"INTERMUD":         "",
+		"SUBGENRE":         "None",
+		"AREAS":            "0",
+		"HELPFILES":        "0",
+		"MOBILES":          "0",
+		"OBJECTS":          "0",
+		"ROOMS":            "1",
+		"CLASSES":          "0",
+		"LEVELS":           "0",
+		"RACES":            "3",
+		"SKILLS":           "900",
+		"ANSI":             "1",
+		"MCCP":             "1",
+		"MCP":              "0",
+		"MSDP":             "0",
+		"MSP":              "0",
+		"MXP":              "0",
+		"PUEBLO":           "0",
+		"UTF-8":            "1",
+		"VT100":            "1",
+		"XTERM 255 COLORS": "1",
+		"PAY TO PLAY":      "0",
+		"PAY FOR PERKS":    "0",
+		"HIRING BUILDERS":  "0",
+		"HIRING CODERS":    "0"}
+}
+
+type Server struct {
+	address    string
+	listener   net.Listener
+	clients    map[int]*Client
+	tickers    map[string]*Ticker
+	alive      bool
+	World      *world.World
+	exitstatus int
+}
+
+type Ticker struct {
+	*time.Ticker
+	Server       *Server
+	Name         string
+	Milliseconds int
+	callback     func(me *Ticker, t time.Time) (stop bool)
+}
+
+const DEFAULT_MOTD_OK = `
+###############################
+#       Workers Of Eruta      # 
+###############################
+
+`
+
+const DEFAULT_MOTD = `
+Welcome!
+`
+
+func (me *Server) SetupWorld() (err error) {
+	me.World, err = world.LoadWorld(me.DataPath(), "WOE")
+
+	if err != nil {
+		monolog.Error("Could not load world WOE: %s", err)
+		return err
+	}
+
+	if me.World == nil {
+		monolog.Info("Creating new default world...")
+		me.World = world.NewWorld("WOE", DEFAULT_MOTD, me.DataPath())
+		err := me.World.Save(me.DataPath())
+		if err != nil {
+			monolog.Error("Could not save world: %v", err)
+			return err
+		} else {
+			monolog.Info("Saved default world.")
+		}
+	}
+	return nil
+}
+
+func NewServer(address string) (server *Server, err error) {
+	listener, err := net.Listen("tcp", address)
+	if err != nil {
+		return nil, err
+	}
+
+	monolog.Info("Server listening on %s.", address)
+
+	clients := make(map[int]*Client)
+	tickers := make(map[string]*Ticker)
+
+	server = &Server{address, listener, clients, tickers, true, nil, STATUS_RESTART}
+	err = server.SetupWorld()
+	if err != nil {
+		monolog.Error("Could not set up or load world!")
+		return nil, err
+	}
+
+	monolog.Info("Server world set up.")
+	server.AddDefaultTickers()
+	monolog.Info("Tickers set up.")
+
+	return server, err
+}
+
+func NewTicker(server *Server, name string, milliseconds int, callback func(me *Ticker, t time.Time) bool) *Ticker {
+	ticker := time.NewTicker(time.Millisecond * time.Duration(milliseconds))
+	return &Ticker{ticker, server, name, milliseconds, callback}
+}
+
+func (me *Ticker) Run() {
+OUTER:
+	for me.Server.alive {
+		for tick := range me.C {
+			if !me.callback(me, tick) {
+				break OUTER
+			}
+		}
+	}
+}
+
+func (me *Server) RemoveTicker(name string) {
+	ticker, have := me.tickers[name]
+	if !have {
+		return
+	}
+	ticker.Stop()
+	delete(me.tickers, name)
+}
+
+func (me *Server) StopTicker(name string) {
+	ticker, have := me.tickers[name]
+	if !have {
+		return
+	}
+	ticker.Stop()
+}
+
+func (me *Server) AddTicker(name string, milliseconds int, callback func(me *Ticker, t time.Time) bool) *Ticker {
+	_, have := me.tickers[name]
+
+	if have {
+		me.RemoveTicker(name)
+	}
+
+	ticker := NewTicker(me, name, milliseconds, callback)
+	me.tickers[name] = ticker
+	go ticker.Run()
+
+	return ticker
+}
+
+func onWeatherTicker(me *Ticker, t time.Time) bool {
+	me.Server.BroadcastToChannel("weather", "The weather is changing...\n")
+	return true
+}
+
+func (me *Server) AddDefaultTickers() {
+	me.AddTicker("weather", 30000, onWeatherTicker)
+}
+
+func (me *Server) handleDisconnectedClients() {
+	for me.alive {
+		time.Sleep(1)
+		for id, client := range me.clients {
+			if !client.IsAlive() {
+				monolog.Info("Client %d has disconnected.", client.id)
+				client.Close()
+				delete(me.clients, id)
+			}
+		}
+	}
+}
+
+func (me *Server) findFreeID() (id int, err error) {
+	for id = 0; id < MAX_CLIENTS; id++ {
+		client, have := me.clients[id]
+		if (!have) || (client == nil) {
+			return id, nil
+		}
+	}
+	return -1, fmt.Errorf("Too many clients!")
+}
+
+func (me *Server) onConnect(conn net.Conn) (err error) {
+	id, err := me.findFreeID()
+	if err != nil {
+		monolog.Info("Refusing connection for %s: too many clients. ", conn.RemoteAddr().String())
+		conn.Close()
+		return nil
+	}
+	monolog.Info("New client connected from %s, id %d. ", conn.RemoteAddr().String(), id)
+	client := NewClient(me, id, conn)
+	me.clients[id] = client
+	return client.Serve()
+}
+
+func (me *Server) Shutdown() {
+	monolog.Info("Server is going to shut down.")
+	me.alive = false
+	me.exitstatus = STATUS_SHUTDOWN
+}
+
+func (me *Server) Restart() {
+	monolog.Info("Server is going to restart.")
+	me.alive = false
+	me.exitstatus = STATUS_RESTART
+}
+
+func (me *Server) Close() {
+	monolog.Info("Closing server, shutting down tickers.")
+
+	for name, _ := range me.tickers {
+		me.RemoveTicker(name)
+	}
+
+	monolog.Info("Closing server, shutting down clients.")
+	for _, client := range me.clients {
+		if client.IsAlive() {
+			client.Close()
+		}
+	}
+
+	me.handleDisconnectedClients()
+	monolog.Info("Closed server.")
+}
+
+func (me *Server) Serve() (status int, err error) {
+	// Setup random seed here, or whatever
+	rand.Seed(time.Now().UTC().UnixNano())
+
+	go me.handleDisconnectedClients()
+
+	for me.alive {
+		if tcplistener, ok := me.listener.(*net.TCPListener); ok {
+			tcplistener.SetDeadline(time.Now().Add(5 * time.Second))
+		}
+		conn, err := me.listener.Accept()
+		if err != nil {
+			if noe, ok := err.(*net.OpError); ok && noe.Timeout() {
+				// it's a timeout. Do nothing, just listen again.
+				// this to allow the alive flag to do it's work.
+			} else {
+				return STATUS_CANNOT_LISTEN, err
+			}
+		} else {
+			go me.onConnect(conn)
+		}
+	}
+	return me.exitstatus, nil
+}
+
+func (me *Server) BroadcastString(message string) {
+	for _, client := range me.clients {
+		if client.IsAlive() {
+			client.WriteString(message)
+		}
+	}
+}
+
+func (me *Server) Broadcast(format string, args ...interface{}) {
+	msg := fmt.Sprintf(format, args...)
+	me.BroadcastString(msg)
+}
+
+func (me *Server) BroadcastStringToChannel(channelname string, message string) {
+	for _, client := range me.clients {
+		if client.IsLoginFinished() && client.IsListeningToChannel(channelname) {
+			client.WriteString(message)
+		}
+	}
+}
+
+func (me *Server) BroadcastToChannel(channelname string, format string, args ...interface{}) {
+	msg := fmt.Sprintf(format, args...)
+	me.BroadcastStringToChannel(channelname, msg)
+}
+
+// Returns the data path of the server
+func (me *Server) DataPath() string {
+	//
+	cwd, err := os.Getwd()
+	monolog.Debug("Current direcory: %s (%v).", cwd, err)
+
+	if err != nil {
+		cwd = "."
+	}
+
+	fp := filepath.Join(cwd, "data", "var")
+	monolog.Debug("Data path: %s (%v). ", fp, err)
+
+	return fp
+}
+
+// Returns the script path of the server
+func (me *Server) ScriptPath() string {
+	//
+	cwd, err := os.Getwd()
+	if err != nil {
+		cwd = "."
+	}
+
+	return filepath.Join(cwd, "data", "script")
+}

+ 0 - 41
src/woe/server/setuptelnet.go → server/setuptelnet.go

@@ -221,45 +221,4 @@ func (me * Client) SetupTelnet() {
 }
 
 
-/*  
-  # Switches to "password" mode.
-  def password_mode
-    # The server sends "IAC WILL ECHO", meaning "I, the server, will do any 
-    # echoing from now on." The client should acknowledge this with an IAC DO 
-    # ECHO, and then stop putting echoed text in the input buffer. 
-    # It should also do whatever is appropriate for password entry to the input 
-    # box thing - for example, it might * it out. Text entered in server-echoes 
-    # mode should also not be placed any command history.
-    # don't use the Q state machne for echos
-    @telnet.telnet_send_bytes(TELNET_IAC, TELNET_WILL, TELNET_TELOPT_ECHO)
-    tev = wait_for_input(0.1)
-    return tev if tev && tev.type != :do
-    return nil
-  end
 
-  # Switches to "normal, or non-password mode.
-  def normal_mode
-    # When the server wants the client to start local echoing again, it sends 
-    # "IAC WONT ECHO" - the client must respond to this with "IAC DONT ECHO".
-    # Again don't use Q state machine.   
-    @telnet.telnet_send_bytes(TELNET_IAC, TELNET_WONT, TELNET_TELOPT_ECHO)
-    tev = wait_for_input(0.1)
-    return tev if tev && tev.type != :dont
-    return nil
-  end
-  
-  end
-
-  
-  def handle_command
-    order = wait_for_command
-    case order
-    when "/quit"
-      write("Byebye!\r\n")
-      @busy = false
-    else
-      @server.broadcast("#{@account.id} said #{order}\r\n")
-    end
-  end
-   
-*/

+ 0 - 0
src/woe/sitef/marshal.go → sitef/marshal.go


+ 0 - 0
src/woe/sitef/sitef.go → sitef/sitef.go


+ 0 - 811
src/woe/server/ask.go

@@ -1,811 +0,0 @@
-package server
-
-/* This file contains dialog helpers for the client. */
-
-// import "github.com/beoran/woe/monolog"
-import t "github.com/beoran/woe/telnet"
-import "github.com/beoran/woe/telnet"
-import "github.com/beoran/woe/world"
-import "github.com/beoran/woe/monolog"
-import "bytes"
-import "strings"
-import "regexp"
-// import "fmt"
-import "strconv"
-// import "strings"
-
-const NEW_CHARACTER_PRICE = 4
-  
-// Switches to "password" mode.
-func (me * Client) PasswordMode() telnet.Event {
-// The server sends "IAC WILL ECHO", meaning "I, the server, will do any 
-// echoing from now on." The client should acknowledge this with an IAC DO 
-// ECHO, and then stop putting echoed text in the input buffer. 
-// It should also do whatever is appropriate for password entry to the input 
-// box thing - for example, it might * it out. Text entered in server-echoes 
-// mode should also not be placed any command history.
-// don't use the Q state machne for echos
-    me.telnet.TelnetSendBytes(t.TELNET_IAC, t.TELNET_WILL, t.TELNET_TELOPT_ECHO)
-    tev, _, _:= me.TryReadEvent(100)
-    if tev != nil && !telnet.IsEventType(tev, t.TELNET_DO_EVENT) { 
-        return tev
-    }
-    return nil
-}
-
-// Switches to "normal, or non-password mode.
-func (me * Client) NormalMode() telnet.Event {
-// When the server wants the client to start local echoing again, it s}s 
-// "IAC WONT ECHO" - the client must respond to this with "IAC DONT ECHO".
-// Again don't use Q state machine.   
-    me.telnet.TelnetSendBytes(t.TELNET_IAC, t.TELNET_WONT, t.TELNET_TELOPT_ECHO)
-    tev, _, _ := me.TryReadEvent(100)
-    if tev != nil && !telnet.IsEventType(tev, t.TELNET_DONT_EVENT) { 
-        return tev
-    }
-    return nil
-}
-
-func (me * Client) Printf(format string, args ...interface{}) {
-    me.telnet.TelnetPrintf(format, args...)
-}
-
-func (me * Client) ColorTest() {
-    me.Printf("\033[1mBold\033[0m\r\n")
-    me.Printf("\033[3mItalic\033[0m\r\n")
-    me.Printf("\033[4mUnderline\033[0m\r\n")
-    for fg := 30; fg < 38; fg++ {
-        me.Printf("\033[%dmForeground Color %d\033[0m\r\n", fg, fg)
-        me.Printf("\033[1;%dmBold Foreground Color %d\033[0m\r\n", fg, fg)
-    }
-    
-    for bg := 40; bg < 48; bg++ {
-        me.Printf("\033[%dmBackground Color %d\033[0m\r\n", bg, bg)
-        me.Printf("\033[1;%dmBold Background Color %d\033[0m\r\n", bg, bg)
-    }    
-}
-
-
-// Blockingly reads a single command from the client
-func (me * Client) ReadCommand() (something []byte) {
-    something = nil
-    for something == nil { 
-        something, _, _ = me.TryRead(-1)
-        if something != nil {
-            something = bytes.TrimRight(something, "\r\n")
-            return something
-        }
-    } 
-    return nil       
-}
-
-func (me * Client) AskSomething(prompt string, re string, nomatch_prompt string, noecho bool) (something []byte) {
-    something = nil
-    
-    if noecho {
-      me.PasswordMode()
-    }
-
-    for something == nil || len(something) == 0 { 
-        me.Printf("%s", prompt)
-        something, _, _ = me.TryRead(-1)
-        if something != nil {
-            something = bytes.TrimRight(something, "\r\n")
-            if len(re) > 0 {
-                ok, _ := regexp.Match(re, something)
-                if !ok {
-                    me.Printf("\n%s\n", nomatch_prompt)
-                    something = nil
-                }
-            }
-        }
-    }
-    
-    if noecho {
-      me.NormalMode()
-      me.Printf("\n")
-    }
-        
-    return something
-}
-
-func (me * Client) AskYesNo(prompt string) bool {
-    res := me.AskSomething(prompt + " (y/n)","[ynYN]", "Please answer y or n.", false)
-    if res[0] == 'Y'|| res[0] == 'y' { 
-        return true
-    } else {
-        return false
-    }
-}
-
-// Interface for an item in a list of options. 
-type AskOption interface {
-    // Name of the option, also used to compare input
-    AskName() string
-    // Short description of the option, shown after name
-    AskShort() string
-    // Long description, displayed if "help name" is requested
-    AskLong() string
-    // Accound privilege required or the option to be selectable.
-    AskPrivilege() world.Privilege
-}
-
-type AskOptionList interface {
-    AskOptionListLen() int
-    AskOptionListGet(index int) AskOption
-}
-
-type TrivialAskOption string
-
-func (me TrivialAskOption) AskName() (string) {
-    return string(me)
-}
-
-func (me TrivialAskOption) AskLong() (string) {
-    return ""
-}
-
-func (me TrivialAskOption) AskShort() (string) {
-    return ""
-}
-
-func (me TrivialAskOption) AskPrivilege() (world.Privilege) {
-    return world.PRIVILEGE_ZERO
-}
-
-type TrivialAskOptionList []TrivialAskOption
-
-func (me TrivialAskOptionList) AskOptionListLen() int {
-    return len(me)
-}
-
-func (me TrivialAskOptionList) AskOptionListGet(index int) AskOption {
-    return me[index]
-}
-
-
-
-type SimpleAskOption struct {
-    Name string
-    Short string
-    Long string
-    Privilege world.Privilege
-}
-
-func (me SimpleAskOption) AskName() string {
-    return me.Name
-}
-
-func (me SimpleAskOption) AskShort() string {
-    return me.Short
-}
-
-func (me SimpleAskOption) AskLong() string {
-    return me.Long
-}
-
-func (me SimpleAskOption) AskPrivilege() world.Privilege {
-    return me.Privilege
-}
-
-type SimpleAskOptionList []SimpleAskOption
-
-func (me SimpleAskOptionList) AskOptionListLen() int {
-    return len(me)
-}
-
-func (me SimpleAskOptionList) AskOptionListGet(index int) AskOption {
-    return me[index]
-}
-
-
-type JobListAsker []world.Job
-
-func (me JobListAsker) AskOptionListLen() int {
-    return len(me)
-}
-
-func (me JobListAsker) AskOptionListGet(index int) AskOption {
-    return me[index]
-}
-
-type KinListAsker []world.Kin
-
-func (me KinListAsker) AskOptionListLen() int {
-    return len(me)
-}
-
-func (me KinListAsker) AskOptionListGet(index int) AskOption {
-    return me[index]
-}
-
-type GenderListAsker []world.Gender
-
-func (me GenderListAsker) AskOptionListLen() int {
-    return len(me)
-}
-
-func (me GenderListAsker) AskOptionListGet(index int) AskOption {
-    return me[index]
-}
-
-
-
-type AskOptionSlice []AskOption
-type AskOptionFilterFunc func(AskOption) (AskOption)
-
-func (me AskOptionSlice) AskOptionListLen() int {
-    return len(me)
-}
-
-func (me AskOptionSlice) AskOptionListGet(index int) AskOption {
-    return me[index]
-}
-
-func AskOptionListEach(me AskOptionList, cb AskOptionFilterFunc) (AskOption) {
-    for i := 0; i <  me.AskOptionListLen() ; i++ {
-        res := cb(me.AskOptionListGet(i))
-        if (res != nil) {
-            return res
-        }
-    }
-    return nil
-}
-
-
-func AskOptionListFilter(me AskOptionList, cb AskOptionFilterFunc) (AskOptionList) {
-    result := make(AskOptionSlice, 0)
-    for i := 0; i <  me.AskOptionListLen() ; i++ {
-        res := cb(me.AskOptionListGet(i))
-        if (res != nil) {
-            result = append(result, res)
-        }
-    }
-    return result
-}
-
-/* Finds the name irrespecful of the case */
-func AskOptionListFindName(me AskOptionList, name string) (AskOption) {
-    return AskOptionListEach(me, func (e AskOption) (AskOption) {
-        if strings.ToLower(e.AskName()) == strings.ToLower(name) {
-            return e
-        } else {
-            return nil
-        }
-    })
-}
-
-
-/* Filters the list by privilege level (only those allowed by the level are retained) */
-func AskOptionListFilterPrivilege(me AskOptionList, privilege world.Privilege) (AskOptionList) {
-    return AskOptionListFilter(me, func (e AskOption) (AskOption) {
-        if (e.AskPrivilege() <= privilege) {
-            return e
-        } else {
-            return nil
-        }
-    })
-}
-
-type MergedAskOptionList struct {
-    head AskOptionList
-    tail AskOptionList
-}
-
-func (me * MergedAskOptionList) AskOptionListLen() int {
-    return me.head.AskOptionListLen() + me.tail.AskOptionListLen()
-}
-
-func (me * MergedAskOptionList) AskOptionListGet(index int) (AskOption) {
-    headlen := me.head.AskOptionListLen()
-    if (index < headlen) {
-        return me.head.AskOptionListGet(index)
-    }
-    return me.tail.AskOptionListGet(index - headlen)
-}
-
-
-/* Merges two AskOptionLists without copying using the MergedAskOptionList rtype  */
-func AskOptionListMerge(me AskOptionList, you AskOptionList) (AskOptionList) {
-    return &MergedAskOptionList{me, you}
-}
-
-
-func (me AskOptionSlice) Each(cb AskOptionFilterFunc) (AskOption) {
-    for i := 0; i <  len(me) ; i++ {
-        res := cb(me[i])
-        if (res != nil) {
-            return res
-        }
-    }
-    return nil
-}
-
-
-func (me AskOptionSlice) Filter(cb AskOptionFilterFunc) (AskOptionSlice) {
-    result := make(AskOptionSlice, 0)
-    for i := 0; i <  len(me) ; i++ {
-        res := cb(me[i])
-        if (res != nil) {
-            result = append(result, res)
-        }
-    }
-    return result
-}
-
-/* Finds the name irrespecful of the case */
-func (me AskOptionSlice) FindName(name string) (AskOption) {
-    return me.Each(func (e AskOption) (AskOption) {
-        if strings.ToLower(e.AskName()) == strings.ToLower(name) {
-            return e
-        } else {
-            return nil
-        }
-    })
-}
-
-
-/* Filters the list by privilege level (only those allowed by the level are retained) */
-func (me AskOptionSlice) FilterPrivilege(privilege world.Privilege) (AskOptionSlice) {
-    return me.Filter(func (e AskOption) (AskOption) {
-        if (e.AskPrivilege() <= privilege) {
-            return e
-        } else {
-            return nil
-        }
-    })
-}
-
-func (me * Client) AskOptionListHelp(alist AskOptionList, input []byte) { 
-    re := regexp.MustCompile("[^ \t,]+")
-    argv  := re.FindAll(input, -1)
-    if (len(argv) < 2) {
-        me.Printf("Help usage: help <topic>.\n")
-        return
-    } 
-    e := AskOptionListFindName(alist, string(argv[1])) 
-    if (e == nil) {
-        me.Printf("Cannot find topic %s in list. No help available.\n", string(argv[1]))
-    } else  {
-        al := e.AskLong()
-        if (al == "") {
-            me.Printf("Topic %s found, but help is unavailable.\n", string(argv[1]))
-        } else {
-            me.Printf("Help on %s:\n%s\n", string(argv[1]), e.AskLong())
-        }
-    }
-}
-
-
-func (me * Client) AskOptionListOnce(heading string,  prompt string, noecho bool, alist AskOptionList) (result AskOption) { 
-    list := AskOptionListFilterPrivilege(alist, me.account.Privilege)
-    me.Printf("\n%s\n\n",heading)
-    for i := 0; i < list.AskOptionListLen(); i++ {
-        v := list.AskOptionListGet(i)
-        sh := v.AskShort() 
-        if sh == "" { 
-            me.Printf("[%d] %s\n", i+1, v.AskName())
-        } else {
-            me.Printf("[%d] %s: %s\n", i+1, v.AskName(), sh)
-        }
-    }
-    me.Printf("\n")
-    aid := me.AskSomething(prompt, "", "", false);
-    iresp, err := strconv.Atoi(string(aid))
-    if err != nil { /* Try by name if not a number. */
-        e := AskOptionListFindName(alist, string(aid))
-        if e != nil {
-            return e
-        } else if ok, _ := regexp.Match("help", bytes.ToLower(aid)) ; ok {
-            me.AskOptionListHelp(list, aid)
-        } else {    
-        me.Printf("Name not found in list. Please choose a number or name from the list above. Or type help <option> for help on that option.\n")
-        }
-    } else if (iresp>0) && (iresp<=list.AskOptionListLen()) { 
-        /* In range of list. */
-        return list.AskOptionListGet(iresp-1)
-    } else {
-        me.Printf("Please choose a number or name from the list above.\n")
-    }
-    return nil
-}
-
-func (me * Client) AskOptionList(
-    heading string, prompt string, noecho bool, 
-    noconfirm bool, list AskOptionList) (result AskOption) {     
-    for {
-        result = me.AskOptionListOnce(heading, prompt, noecho, list)
-        if result != nil {
-            if noconfirm || me.AskYesNo(heading + "\nConfirm " + result.AskName() + "? ") {
-                return result
-            }
-        }
-    }
-}
-
-func (me * Client) AskOptionListExtra(heading string, 
-prompt string, noecho bool, noconfirm bool, list AskOptionList, 
-extra AskOptionList) (result AskOption) {
-    xlist := AskOptionListMerge(list, extra)
-    return me.AskOptionList(heading, prompt, noecho, noconfirm, xlist)
-}
-
-
-
-func (me * Client) AskEntityListOnce(
-        heading string,  prompt string, noecho bool, 
-        elist world.EntitylikeSlice, extras []string) (result world.Entitylike, alternative string) { 
-    list := elist.FilterPrivilege(me.account.Privilege)
-    me.Printf("\n%s\n\n",heading)
-    last := 0
-    for i, v := range(list) {
-        e := v.AsEntity()
-        me.Printf("[%d] %s: %s\n", i+1, e.Name, e.Short)
-        last = i+1
-    }
-    
-    if extras != nil {
-        for i, v := range(extras) {
-            me.Printf("[%d] %s\n", last+i+1, v)
-        }
-    }
-    
-    me.Printf("\n")
-    aid := me.AskSomething(prompt, "", "", false);
-    iresp, err := strconv.Atoi(string(aid))
-    if err != nil { /* Try by name if not a number. */
-        e := list.FindName(string(aid))
-        if e != nil {
-            return e, ""
-        } else {
-            if extras != nil {
-                for _, v := range(extras) {
-                    if strings.ToLower(v) == strings.ToLower(string(aid)) {
-                        return nil, v
-                    }
-                }
-            }
-            me.Printf("Name not found in list. Please choose a number or name from the list above.\n")
-        }
-    } else if (iresp>0) && (iresp<=len(list)) { /* In range of list. */
-        return list[iresp-1], ""
-    } else if (extras != nil) && (iresp>last) && (iresp <= last + len(extras)) { 
-        return nil, extras[iresp - last - 1]
-    } else {
-        me.Printf("Please choose a number or name from the list above.\n")
-    }
-    return nil, ""
-}
-    
-
-func (me * Client) AskEntityList(
-    heading string, prompt string, noecho bool, 
-    noconfirm bool, list world.EntitylikeSlice, extras []string) (result world.Entitylike, alternative string) {     
-    for {
-        result, alternative = me.AskEntityListOnce(heading, prompt, noecho, list, extras)
-        if result != nil {
-            e := result.AsEntity()
-            if (!noconfirm) {
-                me.Printf("\n%s: %s\n\n%s\n\n", e.Name, e.Short, e.Long)
-            } 
-            if noconfirm || me.AskYesNo(heading + "\nConfirm " + e.Name + "? ") {
-                return result, ""
-            }
-        } else if alternative != "" {
-            if noconfirm || me.AskYesNo("Confirm " + alternative + " ?") {
-                return result, alternative
-            }
-        }
-    }
-}
-    
-
-const LOGIN_RE = "^[A-Za-z][A-Za-z0-9]*$"
-
-func (me * Client) AskLogin() []byte {
-    return me.AskSomething("Login?>", LOGIN_RE, "Login must consist of letters followed by letters or numbers.", false)
-}
-
-
-const EMAIL_RE = "@"
-
-func (me * Client) AskEmail() []byte {
-    return me.AskSomething("E-mail?>", EMAIL_RE, "Email must have at least an @ in there somewhere.", false)
-}
-
-const CHARNAME_RE = "^[A-Z][A-Za-z]+$"
-
-
-func (me * Client) AskCharacterName() []byte {
-    return me.AskSomething("Character Name?>", CHARNAME_RE, "Character name consist of a capital letter followed by at least one letter.", false)
-}
-
-func (me * Client) AskPassword() []byte {
-    return me.AskSomething("Password?>", "", "", true)
-}
-
-func (me * Client) AskRepeatPassword() []byte {
-    return me.AskSomething("Repeat Password?>", "", "", true)
-}
-
-func (me * Client) HandleCommand() {
-    command := me.ReadCommand()
-    me.ProcessCommand(command)
-}
- 
-func (me * Client) ExistingAccountDialog() bool {
-    pass  := me.AskPassword()
-    for pass == nil {
-        me.Printf("Password may not be empty!\n")        
-        pass  = me.AskPassword()
-    }
-    
-    if !me.account.Challenge(string(pass)) {
-        me.Printf("Password not correct!\n")
-        me.Printf("Disconnecting!\n")
-        return false
-    }    
-    return true
-}
-
-func (me * Client) NewAccountDialog(login string) bool {
-    for me.account == nil {    
-      me.Printf("\nWelcome, %s! Creating new account...\n", login)
-      pass1  := me.AskPassword()
-      
-      if pass1 == nil { 
-          return false
-      }
-      
-      pass2 := me.AskRepeatPassword()
-      
-      if pass1 == nil { 
-          return false
-      }
-      
-      if string(pass1) != string(pass2) {
-        me.Printf("\nPasswords do not match! Please try again!\n")
-        continue
-      }
-      
-      email := me.AskEmail()
-      if email == nil { return false  }
-      
-      me.account = world.NewAccount(login, string(pass1), string(email), 7)
-      err      := me.account.Save(me.server.DataPath())
-      
-      if err != nil {      
-        monolog.Error("Could not save account %s: %v", login, err)  
-        me.Printf("\nFailed to save your account!\nPlease contact a WOE administrator!\n")
-        return false
-      }
-      
-      monolog.Info("Created new account %s", login)  
-      me.Printf("\nSaved your account.\n")
-      return true
-    }
-    return false
-}
-  
-func (me * Client) AccountDialog() bool {
-    login  := me.AskLogin()
-    if login == nil { return false }
-    var err error
-    
-    if me.server.World.GetAccount(string(login)) != nil {
-        me.Printf("Account already logged in!\n")
-        me.Printf("Disconnecting!\n")
-        return false 
-    }
-    
-    me.account, err = me.server.World.LoadAccount(string(login))    
-    if err != nil {
-        monolog.Warning("Could not load account %s: %v", login, err)  
-    }
-    if me.account != nil {
-      return me.ExistingAccountDialog()
-    } else {
-      return me.NewAccountDialog(string(login))
-    }
-}
-
-func (me * Client) NewCharacterDialog() bool {
-    noconfirm := true
-    extra := TrivialAskOptionList { TrivialAskOption("Cancel") }
-
-    me.Printf("New character:\n")
-    charname := me.AskCharacterName()
-    
-    existing, aname, _ := world.LoadCharacterByName(me.server.DataPath(), string(charname))
-    
-    for (existing != nil) {
-        if (aname == me.account.Name) {
-            me.Printf("You already have a character with a similar name!\n")
-        } else {
-            me.Printf("That character name is already taken by someone else.\n")
-        }
-        charname := me.AskCharacterName()
-        existing, aname, _ = world.LoadCharacterByName(me.server.DataPath(), string(charname))
-    }
-    
-    kinres := me.AskOptionListExtra("Please choose the kin of this character", "Kin?> ", false, noconfirm, KinListAsker(world.KinList), extra)
-
-    if sopt, ok := kinres.(TrivialAskOption) ; ok  {
-        if string(sopt) == "Cancel" { 
-            me.Printf("Character creation canceled.\n")
-            return true
-        } else {
-            return true
-        }
-    }
-    
-    kin := kinres.(world.Kin)
-    
-    genres := me.AskOptionListExtra("Please choose the gender of this character", "Gender?> ", false, noconfirm, GenderListAsker(world.GenderList), extra)
-    if sopt, ok := kinres.(TrivialAskOption) ; ok  {
-        if string(sopt) == "Cancel" { 
-            me.Printf("Character creation canceled.\n")
-            return true
-        } else {
-            return true
-        }
-    }
-     
-    gender := genres.(world.Gender)
-
-
-    jobres := me.AskOptionListExtra("Please choose the job of this character", "Gender?> ", false, noconfirm, JobListAsker(world.JobList), extra)
-    if sopt, ok := kinres.(TrivialAskOption) ; ok  {
-        if string(sopt) == "Cancel" { 
-            me.Printf("Character creation canceled.\n")
-            return true
-        } else {
-            return true
-        }
-    }
-     
-    job := jobres.(world.Job)
-
-    character := world.NewCharacter(me.account, 
-                    string(charname), &kin, &gender, &job)
-    
-    me.Printf("%s", character.Being.ToStatus());
-    
-    ok := me.AskYesNo("Is this character ok?")
-    
-    if (!ok) {
-        me.Printf("Character creation canceled.\n")
-        return true
-    }
-    
-    me.account.AddCharacter(character)
-    me.account.Points -= NEW_CHARACTER_PRICE 
-    me.account.Save(me.server.DataPath())
-    character.Save(me.server.DataPath())
-    me.Printf("Character %s saved.\n", character.Being.Name)
-    
-
-    return true
-}    
-
-
-func (me * Client) DeleteCharacterDialog() (bool) {
-    extra := []AskOption { TrivialAskOption("Cancel"), TrivialAskOption("Disconnect") }
-
-    els := me.AccountCharacterList()
-    els = append(els, extra...)
-    result := me.AskOptionList("Character to delete?", 
-        "Character?>", false, false, els)
-
-    if alt, ok :=  result.(TrivialAskOption) ; ok {
-        if string(alt) == "Disconnect" {
-            me.Printf("Disconnecting")
-            return false
-        } else if string(alt) == "Cancel" {
-            me.Printf("Canceled")
-            return true 
-        } else {
-            monolog.Warning("Internal error, unhandled case.")
-            return true
-        }
-    }
-    
-    
-    character := result.(*world.Character)
-    /* A character that is deleted gives NEW_CHARACTER_PRICE + 
-     * level / (NEW_CHARACTER_PRICE * 2) points, but only after the delete. */
-    np := NEW_CHARACTER_PRICE + character.Level / (NEW_CHARACTER_PRICE * 2)
-    me.account.DeleteCharacter(me.server.DataPath(), character)
-    me.account.Points += np
-    
-    
-    return true
-}
-
-
-
-func (me * Client) AccountCharacterList() AskOptionSlice {
-    els := make(AskOptionSlice, 0, 16)
-    for i:= 0 ; i < me.account.NumCharacters(); i++ {
-        chara := me.account.GetCharacter(i)
-        els = append(els, chara)
-    }
-    return els
-}
-
-func (me * Client) ChooseCharacterDialog() (bool) {
-    extra := []AskOption { 
-        SimpleAskOption{"New", "Create New character", 
-            "Create a new character. This option costs 4 points.",
-             world.PRIVILEGE_ZERO},
-        SimpleAskOption{"Disconnect", "Disconnect from server", 
-            "Disconnect your client from this server.", 
-                        world.PRIVILEGE_ZERO},
-        SimpleAskOption{"Delete", "Delete character", 
-            "Delete a character. A character that has been deleted cannot be reinstated. You will receive point bonuses for deleting your characters that depend on their level.", 
-                        world.PRIVILEGE_ZERO},
-    }
-        
-        
-  
-    var pchara * world.Character = nil
-    
-    for pchara == nil { 
-        els := me.AccountCharacterList()
-        els = append(els, extra...)
-        result := me.AskOptionList("Choose a character?", "Character?>", false, true, els)
-        switch opt := result.(type) { 
-        case SimpleAskOption:
-        if (opt.Name == "New") {
-           if (me.account.Points >= NEW_CHARACTER_PRICE) {
-                if (!me.NewCharacterDialog()) {
-                    return false
-                }
-            } else {
-                me.Printf("Sorry, you have no points left to make new characters!\n")
-            }  
-        } else if opt.Name == "Disconnect" {
-            me.Printf("Disconnecting\n")
-            return false
-        } else if opt.Name == "Delete" {
-            if (!me.DeleteCharacterDialog()) {
-                return false
-            }
-        } else {
-            me.Printf("Internal error, alt not valid: %v.", opt)
-        }
-        case * world.Character: 
-            pchara = opt
-        default:
-            me.Printf("What???")
-        }
-        me.Printf("You have %d points left.\n", me.account.Points)
-    }
-    
-    me.character = pchara
-    me.Printf("%s\n", me.character.Being.ToStatus())
-    me.Printf("Welcome, %s!\n", me.character.Name)
-   
-    return true
-}
- 
-func (me * Client) CharacterDialog() bool {
-    me.Printf("You have %d remaining points.\n", me.account.Points)
-    for me.account.NumCharacters() < 1 {
-        me.Printf("You have no characters yet!\n")
-        if (me.account.Points >= NEW_CHARACTER_PRICE) {
-            if (!me.NewCharacterDialog()) { 
-                return false
-            }
-        } else {
-            me.Printf("Sorry, you have no characters, and no points left to make new characters!\n")
-            me.Printf("Please contact the staff of WOE if you think this is a mistake.\n")
-            me.Printf("Disconnecting!\n")
-            return false 
-        }
-    }
-    
-    return me.ChooseCharacterDialog()
-}
- 
-

+ 0 - 222
src/woe/server/client.go

@@ -1,222 +0,0 @@
-package server
-
-
-import (
-    // "fmt"
-    "net"
-    "time"
-    // "errors"
-    // "io"
-    "github.com/beoran/woe/monolog"
-    "github.com/beoran/woe/telnet"
-    "github.com/beoran/woe/world"
-)
-
-/* Specific properties of a client. */
-type ClientInfo struct {
-    w           int
-    h           int
-    mtts        int
-    naws        bool
-    compress2   bool
-    mssp        bool
-    zmp         bool
-    msp         bool
-    msdp        bool
-    mxp         bool
-    ttype       bool
-    terminals []string
-    terminal    string
-}
-
-type Client struct {
-    server * Server
-    id       int
-    conn     net.Conn
-    alive    bool
-    timeout  int
-    datachan chan []byte
-    errchan  chan error
-    timechan chan time.Time 
-    telnet * telnet.Telnet
-    info     ClientInfo
-    
-    // Account of client or nil if not yet selected.
-    account* world.Account
-    // Character client is plaing with or nil if not yet selected.
-    character * world.Character 
-}
-
-
-func NewClient(server * Server, id int, conn net.Conn) * Client {
-    datachan := make (chan []byte, 1024)
-    errchan  := make (chan error, 1)
-    timechan := make (chan time.Time, 32)
-    telnet   := telnet.New()
-    info     := ClientInfo{w : -1, h : -1, terminal: "none"}
-    return &Client{server, id, conn, true, -1, datachan, errchan, timechan, telnet, info, nil, nil}
-}
-
-func (me * Client) Close() {
-    me.conn.Close()
-    me.alive = false
-    if me.account != nil {     
-        me.server.World.RemoveAccount(me.account.Name)
-    }
-    me.account = nil
-}
-
-/** Goroutine that does the actual reading of input data, and sends it to the 
- * needed channels. */    
-func (me * Client) ServeRead() {
-    for (me.alive) { 
-        buffer  := make([]byte, 1024, 1024)
-        read , err := me.conn.Read(buffer);
-        if err != nil {
-            me.errchan <- err
-            return
-        }
-        // reply will be stored in me.telnet.Events channel
-        me.telnet.ProcessBytes(buffer[:read])
-    }
-}
-
-/* Goroutine that sends any data that must be sent through the Telnet protocol 
- * to the connected client.
- */
- func (me * Client) ServeWrite() {
-     for (me.alive) {
-        select {
-            case data := <- me.telnet.ToClient:
-            monolog.Log("SERVEWRITE","Will send to client: %v", data)
-            me.conn.Write(data)
-        }
-    }
-}
-
-
-func (me * Client) TryReadEvent(millis int) (event telnet.Event, timeout bool, close bool) {
-    var timerchan <-chan(time.Time)
-    
-    if millis >= 0 {
-        timerchan = time.Tick(time.Millisecond * time.Duration(millis))
-    } else {
-        /* If time is negative, block by using a fake time channel that never gets sent anyting */
-        timerchan = make(<-chan(time.Time))
-    }
-    
-    
-    select {
-        case event := <- me.telnet.Events:
-            return event, false, false
-                       
-        case err  := <- me.errchan:
-            monolog.Info("Connection closed: %s\n", err)
-            me.Close()
-            return nil, false, true
-            
-        case _ = <- timerchan:
-            return nil, true, false
-    }
-}
-
-func (me * Client) HandleNAWSEvent(nawsevent * telnet.NAWSEvent) {
-    me.info.w = nawsevent.W
-    me.info.h = nawsevent.H
-    monolog.Info("Client %d window size #{%d}x#{%d}", me.id, me.info.w, me.info.h) 
-    me.info.naws     = true    
-}
-
-func (me * Client) TryRead(millis int) (data []byte, timeout bool, close bool) {
-    
-    for (me.alive) { 
-        event, timeout, close := me.TryReadEvent(millis)
-        if event == nil && (timeout || close) {
-            return nil, timeout, close
-        }
-        switch event := event.(type) {
-            case * telnet.DataEvent:
-                monolog.Log("TELNETDATAEVENT", "Telnet data event %T : %d.", event, len(event.Data))
-                return event.Data, false, false
-            case * telnet.NAWSEvent:
-                monolog.Log("TELNETNAWSEVENT", "Telnet NAWS event %T.", event)
-                me.HandleNAWSEvent(event);
-            default:
-                monolog.Info("Ignoring telnet event %T : %v for now.", event, event)
-        }
-    }
-    
-    return nil, false, true
-}
-
-
-func (me * Client) Serve() (err error) {
-    // buffer := make([]byte, 1024, 1024)
-    go me.ServeWrite()
-    go me.ServeRead()
-    me.SetupTelnet()
-    if me.server.World != nil {
-        me.Printf(me.server.World.MOTD)
-    }
-    if (!me.AccountDialog()) {
-        time.Sleep(3); 
-        // sleep so output gets flushed, hopefully.
-        // Also slow down brute force attacks.
-        me.Close()
-        return nil
-    }
-    
-    if (!me.CharacterDialog()) {
-        time.Sleep(3); 
-        // sleep so output gets flushed, hopefully.
-        // Also slow down brute force attacks.
-        me.Close()
-        return nil
-    }
-    
-    
-    me.Printf("Welcome, %s\n", me.account.Name)
-    
-    for (me.alive) {
-        me.HandleCommand()
-        /*
-        data, _, _ := me.TryRead(3000)
-        
-        if data == nil {
-           // me.telnet.TelnetPrintf("Too late!\r\n")
-        } else {
-            me.server.Broadcast(string(data))
-        }
-        */ 
-        
-    }
-    return nil
-}
-
-func (me * Client) Disconnect() {
-    me.alive = false
-}
-
-func (me * Client) IsAlive() bool {
-    return me.alive
-}
-
-func (me * Client) WriteString(str string) {
-    me.conn.Write([]byte(str))
-}
-
-/** Accessor */
-func (me * Client) GetServer() (* Server) {
-    return me.server;
-}
-
-/** Accessor */
-func (me * Client) GetWorld() (* world.World) {
-    return me.server.World;
-}
-
-/** Accessor */
-func (me * Client) GetAccount() (* world.Account) {
-    return me.account;
-}
-

+ 0 - 355
src/woe/server/server.go

@@ -1,355 +0,0 @@
-package server
-
-import (
-  //  "io"
-    "net"
-  //  "errors"
-    "os"
-    "math/rand"
-    "time"
-    "fmt"
-    "path/filepath"
-    "github.com/beoran/woe/monolog"
-    "github.com/beoran/woe/world"
-)
-
-var MSSP map[string] string
-
-const STATUS_OK             = 0
-const STATUS_CANNOT_LISTEN  = 1
-const STATUS_RESTART        = 0
-const STATUS_SHUTDOWN       = 4
-const MAX_CLIENTS           = 1000
-
-func init() {
-     MSSP = map[string] string {
-          "NAME"        : "Workers Of Eruta",
-          "UPTIME"      : string(time.Now().Unix()),
-          "PLAYERS"     : "0",
-          "CRAWL DELAY" : "0",
-          "CODEBASE"    : "WOE",
-          "CONTACT"     : "beoran@gmail.com",
-          "CREATED"     : "2015",
-           "ICON"       : "None",
-          "LANGUAGE"    : "English",
-          "LOCATION"    : "USA",
-          "MINIMUM AGE" : "18",
-          "WEBSITE"     : "beoran.net",
-          "FAMILY"      : "Custom",
-          "GENRE"       : "Science Fiction",
-          "GAMEPLAY"    : "Adventure",
-          "STATUS"      : "Alpha",
-          "GAMESYSTEM"  : "Custom",
-          "INTERMUD"    : "",
-          "SUBGENRE"    : "None",
-          "AREAS"       : "0",
-          "HELPFILES"   : "0",
-          "MOBILES"     : "0",
-          "OBJECTS"     : "0",
-          "ROOMS"       : "1",
-          "CLASSES"     : "0",
-          "LEVELS"      : "0",
-          "RACES"       : "3",
-          "SKILLS"      : "900",
-          "ANSI"        : "1",
-          "MCCP"        : "1",
-          "MCP"         : "0",
-          "MSDP"        : "0",
-          "MSP"         : "0",
-          "MXP"         : "0",
-          "PUEBLO"      : "0",
-          "UTF-8"       : "1",
-          "VT100"       : "1",
-          "XTERM 255 COLORS" : "1",
-          "PAY TO PLAY"      : "0",
-          "PAY FOR PERKS"    : "0",
-          "HIRING BUILDERS"  : "0",
-          "HIRING CODERS"    : "0" }  
-}
-
-
-
-
-type Server struct {
-    address               string
-    listener              net.Listener
-    clients map[int]    * Client 
-    tickers map[string] * Ticker
-    alive                 bool
-    World               * world.World
-    exitstatus            int
-}
-
-
-type Ticker struct {
-    * time.Ticker
-    Server        * Server
-    Name            string
-    Milliseconds    int
-    callback        func(me * Ticker, t time.Time) (stop bool)
-}
-
-
-const DEFAULT_MOTD_OK =
-
-`
-###############################
-#       Workers Of Eruta      # 
-###############################
-
-`
-
-const DEFAULT_MOTD =
-`
-Welcome!
-`
-
-
-
-func (me * Server) SetupWorld() (err error) {
-    me.World, err = world.LoadWorld(me.DataPath(), "WOE")
-    
-    if err != nil { 
-        monolog.Error("Could not load world WOE: %s", err)
-        return err
-    }
-    
-    if me.World == nil {
-        monolog.Info("Creating new default world...")
-        me.World = world.NewWorld("WOE", DEFAULT_MOTD, me.DataPath())
-        err := me.World.Save(me.DataPath())
-        if err != nil {
-            monolog.Error("Could not save world: %v", err)
-            return err
-        } else {
-            monolog.Info("Saved default world.")
-        }
-    }
-    return nil
-}
-
-
-func NewServer(address string) (server * Server, err error) {
-    listener, err := net.Listen("tcp", address);
-    if (err != nil) { 
-        return nil, err
-    }
-    
-    monolog.Info("Server listening on %s.", address)
-    
-    clients := make(map[int] * Client)
-    tickers := make(map[string] * Ticker)
-
-    server = &Server{address, listener, clients, tickers, true, nil, STATUS_RESTART}
-    err = server.SetupWorld()
-    if err != nil {
-        monolog.Error("Could not set up or load world!")
-        return nil, err
-    }
-    
-    monolog.Info("Server world set up.")    
-    server.AddDefaultTickers()
-    monolog.Info("Tickers set up.")
-
-    return server, err
-}
-
-
-
-func NewTicker(server * Server, name string, milliseconds int, callback func (me * Ticker, t time.Time) bool) (* Ticker) {
-    ticker := time.NewTicker(time.Millisecond * time.Duration(milliseconds))
-    return &Ticker {ticker, server, name, milliseconds, callback}
-}
-
-
-func (me * Ticker) Run() {
-    OUTER: 
-    for me.Server.alive {
-        for tick := range me.C {
-            if (!me.callback(me, tick)) {
-                break OUTER;
-            }
-        }
-    }
-}
-
-
-func (me * Server) RemoveTicker(name string) {
-    ticker, have := me.tickers[name]
-    if (!have) {
-        return
-    }    
-    ticker.Stop()
-    delete(me.tickers, name)
-}
-
-func (me * Server) StopTicker(name string) {
-    ticker, have := me.tickers[name]
-    if (!have) {
-        return
-    }    
-    ticker.Stop();
-}
-
-func (me * Server) AddTicker(name string, milliseconds int, callback func (me * Ticker, t time.Time) bool) (* Ticker) {
-    _, have := me.tickers[name]
-    
-    if have {
-        me.RemoveTicker(name)
-    }
-        
-    ticker := NewTicker(me, name, milliseconds, callback)
-    me.tickers[name] = ticker
-    go ticker.Run();
-    
-    return ticker
-}
-
-
-func onWeatherTicker (me * Ticker, t time.Time) bool {
-    me.Server.Broadcast("The weather is changing...\n")
-    return true
-}
-
-
-func (me * Server) AddDefaultTickers() {
-    me.AddTicker("weather", 30000, onWeatherTicker)    
-}
-
-func (me * Server) handleDisconnectedClients() {
-    for me.alive { 
-        time.Sleep(1)
-        for id, client := range me.clients {
-            if (!client.IsAlive()) {
-                monolog.Info("Client %d has disconnected.", client.id)
-                client.Close()
-                delete(me.clients, id);
-            }
-        }   
-    }
-}
-
-func (me * Server) findFreeID() (id int, err error) {
-    for id = 0 ; id < MAX_CLIENTS ; id++ {
-        client, have := me.clients[id]
-        if (!have) || (client == nil) {
-            return id, nil
-        }
-    }
-    return -1, fmt.Errorf("Too many clients!");
-}
-
-func (me * Server) onConnect(conn net.Conn) (err error) {
-    id, err := me.findFreeID()
-    if err != nil {
-        monolog.Info("Refusing connection for %s: too many clients. ", conn.RemoteAddr().String())
-        conn.Close()
-        return nil
-    }
-    monolog.Info("New client connected from %s, id %d. ", conn.RemoteAddr().String(), id)
-    client := NewClient(me, id, conn)
-    me.clients[id] = client
-    return client.Serve()
-}
-
-func (me * Server) Shutdown() {
-    monolog.Info("Server is going to shut down.")
-    me.alive        = false
-    me.exitstatus   = STATUS_SHUTDOWN
-}
-
-func (me * Server) Restart() {
-    monolog.Info("Server is going to restart.")
-    me.alive        = false
-    me.exitstatus   = STATUS_RESTART
-}
-
-
-func (me * Server) Close() {
-    monolog.Info("Closing server, shutting down tickers.")
-    
-    for name, _ := range me.tickers {
-        me.RemoveTicker(name);
-    }
-
-    monolog.Info("Closing server, shutting down clients.")
-    for _, client := range me.clients {
-        if (client.IsAlive()) {
-            client.Close()
-        }
-    }
-    
-    me.handleDisconnectedClients()
-    monolog.Info("Closed server.")
-}
-
-func (me * Server) Serve() (status int, err error) { 
-    // Setup random seed here, or whatever
-    rand.Seed(time.Now().UTC().UnixNano())
-    
-    go me.handleDisconnectedClients()
-    
-    for (me.alive) {
-        if tcplistener, ok := me.listener.(*net.TCPListener) ; ok {
-          tcplistener.SetDeadline(time.Now().Add(5*time.Second))
-        }
-        conn, err := me.listener.Accept()
-        if err != nil {
-            if noe, ok := err.(*net.OpError) ; ok && noe.Timeout() {
-                // it's a timeout. Do nothing, just listen again.
-                // this to allow the alive flag to do it's work.
-            } else {
-                return STATUS_CANNOT_LISTEN, err
-            }
-        } else {
-            go me.onConnect(conn)
-        }
-    }
-    return me.exitstatus, nil
-}
-
-
-func (me * Server) BroadcastString(message string) {
-    for _, client := range me.clients {
-        if (client.IsAlive()) {
-            client.WriteString(message)
-        }
-    }       
-}
-
-func (me * Server) Broadcast(format string, args ...interface{}) {
-    msg := fmt.Sprintf(format, args...)
-    me.BroadcastString(msg)
-}
-
-
-// Returns the data path of the server
-func (me * Server) DataPath() string {
-    // 
-    cwd, err := os.Getwd();
-    monolog.Debug("Current direcory: %s (%v).", cwd, err)
-    
-    if  err != nil {
-        cwd = "."
-    }
-    
-    fp := filepath.Join(cwd, "data", "var")
-    monolog.Debug("Data path: %s (%v). ", fp, err)
-
-    return fp
-}
-
-// Returns the script path of the server
-func (me * Server) ScriptPath() string {
-    // 
-    cwd, err := os.Getwd();
-    if err != nil {
-        cwd = "."
-    }
-    
-    return filepath.Join(cwd, "data", "script")
-}
-
-
-
-

+ 0 - 111
src/woe/woe.go

@@ -1,111 +0,0 @@
-package main
-
-// import "fmt"
-import "github.com/beoran/woe/server"
-import "github.com/beoran/woe/monolog"
-import "os"
-import "os/exec"
-import "flag"
-import "fmt"
-
-
-type serverLogLevels []string;
-
-var server_loglevels serverLogLevels = serverLogLevels { 
-    "FATAL", "ERROR", "WARNING", "INFO",
-}
-
-
-
-/* Command line flags. */
-var server_mode  = flag.Bool("s", false, "Run in server mode");
-var server_tcpip = flag.String("l", ":7000", "TCP/IP Address where the server will listen");
-var enable_logs  = flag.String("el", "FATAL,ERROR,WARNING,INFO", "Log levels to enable");
-var disable_logs = flag.String("dl", "", "Log levels to disable");
-
-
-func enableDisableLogs() {
-    monolog.EnableLevels(*enable_logs)
-    monolog.EnableLevels(*disable_logs)
-}
- 
-/* Need to restart the server or not? */
-var server_restart = true
-
-func runServer() (status int) {
-    monolog.Setup("woe.log", true, false)    
-    defer monolog.Close()
-    enableDisableLogs()
-    monolog.Info("Starting WOE server...")
-    monolog.Info("Server will run at %s.", *server_tcpip)
-    woe, err := server.NewServer(*server_tcpip)
-    if err != nil {
-        monolog.Error("Could not initialize server!")
-        monolog.Error(err.Error())
-        panic(err)
-    }
-    monolog.Info("Server at %s init ok.", *server_tcpip)
-    defer woe.Close()
-    status, err = woe.Serve()
-    if err != nil {
-        monolog.Error("Error while running WOE server!")
-        monolog.Error(err.Error())
-        panic(err)
-    }
-    monolog.Info("Server shut down without error indication.", *server_tcpip)
-    return status
-}
-
-
-
-func runSupervisor() (status int) {
-    monolog.Setup("woe.log", true, false)
-    defer monolog.Close()
-    enableDisableLogs()
-    monolog.Info("Starting WOE supervisor.")
-    for (server_restart) {
-        // wd  , _ := os.Getwd()
-        exe  := fmt.Sprintf("%s", os.Args[0]) 
-        argp := fmt.Sprintf("-l=%s", *server_tcpip)
-        argel:= fmt.Sprintf("-el=%s", *enable_logs)
-        argdl:= fmt.Sprintf("-dl=%s", *disable_logs)
-        cmd  := exec.Command(exe, "-s=true", argp, argel, argdl)
-        monolog.Info("Starting server %s at %s.", exe, *server_tcpip)
-        cmd.Stderr = os.Stderr
-        cmd.Stdout = os.Stdout
-        monolog.Debug("Server command line: %s.", cmd.Args)
-        err  := cmd.Run()
-        monolog.Info("Server at %s shut down.", *server_tcpip)
-        if (err != nil ) { 
-            monolog.Error("Server shut down with error %s!", err)
-            server_restart = false;
-            return 1
-        }
-    }
-    return 0
-}
-
-
-
-/* Woe can be run in supervisor mode (the default) or server mode (-s).
- * Server mode is the mode in which the real server is run. In supervisor mode, 
- * woe runs a single woe server in server mode using os/exec. This is used to 
- * be able to restart the server gracefully on recompile of the sources. 
- */
-func main() {
-    defer func () { 
-        pani := recover()
-        if (pani != nil) {
-            monolog.Fatal("Panic: %s", pani)
-            os.Exit(255)
-        }
-    } ()
-    
-    flag.Parse()
-    if *server_mode {
-        os.Exit(runServer())
-    } else {
-        os.Exit(runSupervisor())
-    }
-}
-

+ 0 - 0
src/woe/telnet/codes.go → telnet/codes.go


+ 0 - 0
src/woe/telnet/rfc1143.go → telnet/rfc1143.go


+ 0 - 0
src/woe/telnet/telnet.go → telnet/telnet.go


+ 112 - 0
woe.go

@@ -0,0 +1,112 @@
+package main
+
+// import "fmt"
+import "github.com/beoran/woe/server"
+import "github.com/beoran/woe/monolog"
+import "github.com/beoran/woe/raku"
+import "os"
+import "os/exec"
+import "flag"
+import "fmt"
+
+type serverLogLevels []string
+
+var server_loglevels serverLogLevels = serverLogLevels{
+	"FATAL", "ERROR", "WARNING", "INFO",
+}
+
+/* Command line flags. */
+var server_mode = flag.Bool("s", false, "Run in server mode")
+var raku_mode = flag.Bool("r", false, "Run in Raku interpreter mode")
+var server_tcpip = flag.String("l", ":7000", "TCP/IP Address where the server will listen")
+var enable_logs = flag.String("el", "FATAL,ERROR,WARNING,INFO", "Log levels to enable")
+var disable_logs = flag.String("dl", "", "Log levels to disable")
+
+func enableDisableLogs() {
+	monolog.EnableLevels(*enable_logs)
+	monolog.EnableLevels(*disable_logs)
+}
+
+/* Need to restart the server or not? */
+var server_restart = true
+
+func runServer() (status int) {
+	monolog.Setup("woe.log", true, false)
+	defer monolog.Close()
+	enableDisableLogs()
+	monolog.Info("Starting WOE server...")
+	monolog.Info("Server will run at %s.", *server_tcpip)
+	woe, err := server.NewServer(*server_tcpip)
+	if err != nil {
+		monolog.Error("Could not initialize server!")
+		monolog.Error(err.Error())
+		panic(err)
+	}
+	monolog.Info("Server at %s init ok.", *server_tcpip)
+	defer woe.Close()
+	status, err = woe.Serve()
+	if err != nil {
+		monolog.Error("Error while running WOE server!")
+		monolog.Error(err.Error())
+		panic(err)
+	}
+	monolog.Info("Server shut down without error indication.", *server_tcpip)
+	return status
+}
+
+func runSupervisor() (status int) {
+	monolog.Setup("woe.log", true, false)
+	defer monolog.Close()
+	enableDisableLogs()
+	monolog.Info("Starting WOE supervisor.")
+	for server_restart {
+		// wd  , _ := os.Getwd()
+		exe := fmt.Sprintf("%s", os.Args[0])
+		argp := fmt.Sprintf("-l=%s", *server_tcpip)
+		argel := fmt.Sprintf("-el=%s", *enable_logs)
+		argdl := fmt.Sprintf("-dl=%s", *disable_logs)
+		cmd := exec.Command(exe, "-s=true", argp, argel, argdl)
+		monolog.Info("Starting server %s at %s.", exe, *server_tcpip)
+		cmd.Stderr = os.Stderr
+		cmd.Stdout = os.Stdout
+		monolog.Debug("Server command line: %s.", cmd.Args)
+		err := cmd.Run()
+		monolog.Info("Server at %s shut down.", *server_tcpip)
+		if err != nil {
+			monolog.Error("Server shut down with error %s!", err)
+			server_restart = false
+			return 1
+		}
+	}
+	return 0
+}
+
+func runRaku() (status int) {
+	lexer := raku.OpenLexer(os.Stdin)
+	_ = lexer
+	return 0
+}
+
+/* Woe can be run in supervisor mode (the default) or server mode (-s).
+ * Server mode is the mode in which the real server is run. In supervisor mode,
+ * woe runs a single woe server in server mode using os/exec. This is used to
+ * be able to restart the server gracefully on recompile of the sources.
+ */
+func main() {
+	defer func() {
+		pani := recover()
+		if pani != nil {
+			monolog.Fatal("Panic: %s", pani)
+			os.Exit(255)
+		}
+	}()
+
+	flag.Parse()
+	if *server_mode {
+		os.Exit(runServer())
+	} else if *raku_mode {
+		os.Exit(runRaku())
+	} else {
+		os.Exit(runSupervisor())
+	}
+}

+ 13 - 3
src/woe/world/account.go → world/account.go

@@ -43,16 +43,26 @@ func SavePathFor(dirname string, typename string, name string) string {
 
 
 
-func NewAccount(name string, pass string, email string, points int) (*Account) {
-    return &Account{name, pass, "plain", email, points, PRIVILEGE_NORMAL, nil, nil}
+func NewAccount(name string, pass string, email string, points int) (*Account) {    
+    hash := WoeCryptPassword(pass, "")
+    return &Account{name, hash, "woe", email, points, PRIVILEGE_NORMAL, nil, nil}    
+    // return &Account{name, pass, "plain", email, points, PRIVILEGE_NORMAL, nil, nil}
+}
+
+// Export an account to a "universal" map
+func (me * Account) ToUniMap() map[string] interface{} {
+    res := make(map[string] interface{})
+    return res
 }
 
 // Password Challenge for an account.
 func (me * Account) Challenge(challenge string) bool {
     if me.Algo == "plain" {
         return me.Hash == challenge
+    } else if me.Algo == "woe" {
+        return WoeCryptChallenge(me.Hash, challenge)
     }
-    // XXX implement encryption later
+    // implement beter passwd encryption later
     return false
 }
 

+ 0 - 0
src/woe/world/art.go → world/art.go


+ 0 - 1
src/woe/world/being.go → world/being.go

@@ -1241,7 +1241,6 @@ func (me * Being) SaveSitef(rec * sitef.Record) (err error) {
         rec.Put("room", me.Room.ID)
     }
     
-    // TODO: saving: me.Being.SaveSitef(rec)
     return nil
 }
 

+ 0 - 0
src/woe/world/character.go → world/character.go


+ 0 - 0
src/woe/world/entity.go → world/entity.go


+ 0 - 0
src/woe/world/inventory.go → world/inventory.go


+ 0 - 0
src/woe/world/item.go → world/item.go


+ 0 - 0
src/woe/world/mobile.go → world/mobile.go


+ 0 - 0
src/woe/world/room.go → world/room.go


+ 36 - 0
world/security.go

@@ -0,0 +1,36 @@
+package world
+
+import "math/rand"
+import "crypto/sha1"
+import "fmt"
+
+
+
+const MAKE_SALT_AID string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789."
+
+
+func MakeSalt() string {    
+    c1 := MAKE_SALT_AID[rand.Intn(len(MAKE_SALT_AID))]
+    c2 := MAKE_SALT_AID[rand.Intn(len(MAKE_SALT_AID))]
+    res := string( []byte{ c1, c2 })
+    return res
+}
+
+func WoeCryptPassword(password string, salt string) string {
+    if len(salt) < 1 {
+        salt = MakeSalt()
+    }
+    to_hash := salt + password
+    return salt + fmt.Sprintf("%x", sha1.Sum([]byte(to_hash)))
+}
+
+
+func WoeCryptChallenge(hash, trypass string) bool {
+    salt := hash[0:2]
+    try  := WoeCryptPassword(trypass, salt)
+    return try == hash
+}
+
+
+
+

+ 0 - 0
src/woe/world/skill.go → world/skill.go


+ 0 - 0
src/woe/world/technique.go → world/technique.go


+ 0 - 0
src/woe/world/world.go → world/world.go


+ 0 - 0
src/woe/world/zone.go → world/zone.go