Beispiel #1
0
// FromLinesAndFormats generates a *VT100 whose state is set according
// to s (for content) and a (for attributes).
//
// Dimensions are set to the width of s' first line and the height of the
// number of lines in s.
//
// If a is nil, the default attributes are used.
func FromLinesAndFormats(s string, a [][]vt100.Format) *vt100.VT100 {
	lines := strings.Split(s, "\n")
	v := vt100.NewVT100(len(lines), utf8.RuneCountInString(lines[0]))
	for y := 0; y < v.Height; y++ {
		x := 0
		for _, r := range lines[y] {
			v.Content[y][x] = r
			if a != nil {
				v.Format[y][x] = a[y][x]
			}
			x++
		}
	}
	return v
}
Beispiel #2
0
// NewGame makes a new Game. The game immediately starts parsing the Nethack input
// stream and building a model of the world. It also takes over control of the output
// stream to nethack. From this point, your only interaction with these IO objects
// should be through this instance of Game.
//
// It is safe to examine this between calls to Do, but not during any given Do
// call.
func NewGame(in io.Reader, out io.Writer, win WindowSize) (*Game, error) {
	if win.Y < 24 || win.X < 80 {
		panic(fmt.Errorf("screen dimensions must be at least 24x80 (got: %dx%d)", win.Y, win.X))
	}

	cmds, errs := inputUntilClosed(in)
	g := &Game{
		Level:         make(map[level.LevelID]*level.Level),
		Events:        NewTurnEventsList(),
		out:           out,
		vt:            vt100.NewVT100(win.Y, win.X),
		inputCommands: cmds,
		inputErrs:     errs,
	}

	return g, g.waitIdle()
}
Beispiel #3
0
func main() {
	flag.Parse()

	origAttr, err := makeRaw(os.Stdout.Fd())
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := tcsetattr(os.Stdout.Fd(), origAttr); err != nil {
			glog.Error(err) // Nothing much we can do about this error.
		}
	}()

	c := exec.Command("nethack")
	c.Env = append(c.Env, os.Environ()...)
	// I have no idea if this is right, but it seems to work.
	c.Env = append(c.Env, "TERM=xterm")
	pty, err := pty.Start(c)
	if err != nil {
		panic(err)
	}

	v := vt{vt100.NewVT100(24, 80), new(sync.Mutex)}
	vtexport.Export("/debug/vt100", v.VT100, v)
	server := http.Server{
		Addr:    fmt.Sprintf(":%d", port),
		Handler: http.DefaultServeMux,
	}

	vReaderRaw, vWriter := io.Pipe()
	vReader := bufio.NewReader(vReaderRaw)

	dupOut, err := ioutil.TempFile("", "nh_output.txt")
	if err != nil {
		panic(err)
	}

	exit := make(chan struct{}, 0)
	go func() {
		for {
			_, err := io.Copy(pty, os.Stdin)
			if err != nil {
				if err != io.EOF {
					glog.Error(err)
				}
				return
			}
		}
	}()

	go func() {
		defer func() { exit <- struct{}{} }()
		multi := io.MultiWriter(os.Stdout, dupOut, vWriter)
		for {
			_, err := io.Copy(multi, pty)
			if err != nil {
				if err != io.EOF {
					glog.Error(err)
				}
				return
			}
		}
	}()

	go func() {
		for {
			cmd, err := vt100.Decode(vReader)
			if err == nil {
				v.Lock()
				err = v.Process(cmd)
				v.Unlock()
			}
			if err == nil {
				continue
			}
			if _, isUnsupported := err.(vt100.UnsupportedError); isUnsupported {
				// This gets exported through an expvar.
				continue
			}
			if err != io.EOF {
				glog.Error(err)
			}
			return
		}
	}()

	downServer, err := httpdown.HTTP{
		StopTimeout: time.Second * 5,
	}.ListenAndServe(&server)
	if err != nil {
		panic(err)
	}

	<-exit
	downServer.Stop()
	downServer.Wait()
}