func (p *Program) Start() (err error) {
	if !p.IsEnabled() {
		logging.Error("Server %s is not enabled, cannot start", p.Id())
		return pufferd.ErrServerDisabled
	}
	if running, err := p.IsRunning(); running || err != nil {
		return err
	}
	logging.Debug("Starting server %s", p.Id())
	p.Environment.DisplayToConsole(true, "Starting server\n")
	data := make(map[string]interface{})
	for k, v := range p.Variables {
		data[k] = v.Value
	}
	process, err := operations.GenerateProcess(p.Execution.PreExecution, p.Environment, p.DataToMap(), p.Execution.EnvironmentVariables)
	if err != nil {
		logging.Exception("Error generating pre-execution steps", err)
		p.Environment.DisplayToConsole(true, "Error running pre execute\n")
		p.Environment.DisplayToConsole(true, "%s\n", err.Error())
		return
	}
	err = process.Run(p.Environment)
	if err != nil {
		logging.Exception("Error running pre-execution steps", err)
		p.Environment.DisplayToConsole(true, "Error running pre execute\n")
		p.Environment.DisplayToConsole(true, "%s\n", err.Error())
		return
	}
	data["rootDir"] = p.Environment.GetRootDirectory()
	err = p.Environment.ExecuteAsync(p.Execution.ProgramName, apufferi.ReplaceTokensInArr(p.Execution.Arguments, data), apufferi.ReplaceTokensInMap(p.Execution.EnvironmentVariables, data), p.afterExit)
	if err != nil {
		logging.Exception("error starting server "+p.Id(), err)
		p.Environment.DisplayToConsole(true, " Failed to start server\n")
		p.Environment.DisplayToConsole(true, "%s\n", err.Error())
	}
	return
}
Starts the program.
This includes starting the environment if it is not running.
package programs
import (
	"container/list"
	"encoding/json"
	"github.com/pufferpanel/apufferi/v4"
	"github.com/pufferpanel/apufferi/v4/logging"
	"github.com/pufferpanel/pufferd/v2"
	"github.com/pufferpanel/pufferd/v2/environments/envs"
	"github.com/pufferpanel/pufferd/v2/messages"
	"github.com/pufferpanel/pufferd/v2/programs/operations"
	"github.com/spf13/viper"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
	"time"
)
type Program struct {
	apufferi.Server
	CrashCounter int
	Environment  envs.Environment
}
var queue *list.List
var lock = sync.Mutex{}
var ticker *time.Ticker
var running = false
func InitService() {
	queue = list.New()
	ticker = time.NewTicker(1 * time.Second)
	running = true
	go processQueue()
}
func StartViaService(p *Program) {
	lock.Lock()
	defer func() {
		lock.Unlock()
	}()
	if running {
		queue.PushBack(p)
	}
}
func ShutdownService() {
	lock.Lock()
	defer func() {
		lock.Unlock()
	}()
	running = false
	ticker.Stop()
}
func processQueue() {
	for range ticker.C {
		lock.Lock()
		next := queue.Front()
		if next != nil {
			queue.Remove(next)
		}
		lock.Unlock()
		if next == nil {
			continue
		}
		program := next.Value.(*Program)
		if run, _ := program.IsRunning(); !run {
			err := program.Start()
			if err != nil {
				logging.Exception("Error starting server "+program.Id(), err)
			}
		}
	}
}
type FileData struct {
	Contents      io.ReadCloser
	ContentLength int64
	FileList      []messages.FileDesc
	Name          string
}
func (p *Program) DataToMap() map[string]interface{} {
	var result = make(map[string]interface{}, len(p.Variables))
	for k, v := range p.Variables {
		result[k] = v.Value
	}
	return result
}
func CreateProgram() *Program {
	return &Program{
		Server: apufferi.Server{
			Execution: apufferi.Execution{
				Disabled:                false,
				AutoStart:               false,
				AutoRestartFromCrash:    false,
				AutoRestartFromGraceful: false,
				PreExecution:            make([]interface{}, 0),
				PostExecution:           make([]interface{}, 0),
				EnvironmentVariables:    make(map[string]string, 0),
			},
			Type:           "standard",
			Variables:      make(map[string]apufferi.Variable, 0),
			Display:        "Unknown server",
			Installation:   make([]interface{}, 0),
			Uninstallation: make([]interface{}, 0),
		},
	}
}